mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 525024ac56 | |||
| 2383f9ae77 | |||
| 14ec18dfef | |||
| 2f492c8fa8 | |||
| 9db43cd2f4 | |||
| 344c238f73 | |||
| 3376aa995a | |||
| ec75f1b6eb | |||
| 5f2b20ea41 | |||
| 9d130c0ff3 | |||
| 119c41dc86 | |||
| c5613a6fea | |||
| 479bd50540 | |||
| 502a4b8d39 | |||
| 1f85c07453 | |||
| 53ec1e780e | |||
| 2de3def32e | |||
| 61b3c63bbf | |||
| 348eb3ac00 | |||
| 9972e0ee46 | |||
| de971b707b | |||
| 0dcc06f712 | |||
| 109b0788a8 | |||
| d190ebe208 | |||
| fa93af2707 | |||
| 6b59966816 | |||
| b065c9d6fb | |||
| 49d805a60c | |||
| a9a98bf3a4 | |||
| 840d416282 | |||
| dc2cc4c7c8 | |||
| 3d51d2a38d | |||
| 1a31819431 | |||
| 4dfb1ca630 | |||
| e9c8686960 | |||
| 9be97f5213 | |||
| 721d582dcd | |||
| 443f76f3f9 | |||
| 3ad9c49f53 | |||
| 61077ed5a4 | |||
| 1fe3706d0f | |||
| 16df8fd173 | |||
| f7806d4d3c | |||
| 04e44e174e | |||
| f74fe19b77 | |||
| e7487069a5 | |||
| 9c48b80515 | |||
| 814f4296a9 | |||
| 6fa0e7d56a | |||
| 12726338ef | |||
| f3a8e3cfb1 | |||
| 1ac8ba8a24 | |||
| 7b5bb3a152 | |||
| ce7753744a | |||
| 3adaa16537 | |||
| aa4f4c9c66 | |||
| ef01d52905 | |||
| 892de712b2 | |||
| 79aa25f14c | |||
| 165b61329c | |||
| c251e5ace2 | |||
| 550e221265 | |||
| 7110897e9d | |||
| 74badd8a01 | |||
| 95eccffa4a | |||
| 35a190b02f | |||
| 92457ae0fb | |||
| 73d3dd1cd4 | |||
| cb9cd46261 | |||
| 4bd3943345 | |||
| 30bdc7fc3d | |||
| a1d31c1ae2 | |||
| 90fb3095bd | |||
| 0c468369cd | |||
| f88b0c642a | |||
| 1b8bdbe7ed | |||
| 185b5da4d5 | |||
| cd92464467 | |||
| 59e18c6be2 |
@@ -147,3 +147,10 @@ buildNumber.properties
|
||||
|
||||
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
|
||||
!/.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
######################
|
||||
###### Custom ######
|
||||
######################
|
||||
|
||||
# Deployment folder
|
||||
deploy/
|
||||
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to build all Maven profiles and copy zip files to deployment folder
|
||||
# Usage: ./build-all-profiles.sh [to-deploy-folder]
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Parse arguments
|
||||
DEPLOY_DIR="${1:-}"
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SWING_MODULE_DIR="${PROJECT_DIR}/diylc-swing"
|
||||
DEPLOY_DIR="${DEPLOY_DIR:-${PROJECT_DIR}/deploy}"
|
||||
|
||||
# List of profiles to build
|
||||
PROFILES=(
|
||||
"universal-zip"
|
||||
"linux-appimage"
|
||||
"win64-zip"
|
||||
"win64-installer"
|
||||
"mac-x86"
|
||||
"mac-arm"
|
||||
)
|
||||
|
||||
echo "=========================================="
|
||||
echo "Building all Maven profiles"
|
||||
echo "=========================================="
|
||||
echo "Project directory: ${PROJECT_DIR}"
|
||||
echo "Swing module directory: ${SWING_MODULE_DIR}"
|
||||
echo "Deployment directory: ${DEPLOY_DIR}"
|
||||
echo "Tests: Skipped"
|
||||
echo ""
|
||||
|
||||
# Create deployment directory if it doesn't exist
|
||||
mkdir -p "${DEPLOY_DIR}"
|
||||
|
||||
# Change to project root directory
|
||||
cd "${PROJECT_DIR}"
|
||||
|
||||
# Build each profile (clean before each one)
|
||||
TARGET_DIR="${SWING_MODULE_DIR}/target"
|
||||
TOTAL_ZIP_COUNT=0
|
||||
|
||||
for profile in "${PROFILES[@]}"; do
|
||||
echo "----------------------------------------"
|
||||
echo "Building profile: ${profile}"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Clean and build from root, specifying the swing module and profile
|
||||
# This ensures dependencies (diylc-core, diylc-library) are built first
|
||||
mvn clean package -pl diylc-swing -am -P"${profile}" -DskipTests || {
|
||||
echo "ERROR: Failed to build profile ${profile}"
|
||||
echo "Continuing with next profile..."
|
||||
continue
|
||||
}
|
||||
|
||||
echo "✓ Profile ${profile} built successfully"
|
||||
|
||||
# Copy zip and exe files immediately after each build (before next clean)
|
||||
if [ -d "${TARGET_DIR}" ]; then
|
||||
echo "Looking for artifacts in ${TARGET_DIR}..."
|
||||
PROFILE_FILE_COUNT=0
|
||||
|
||||
# Find all zip files in target directory (excluding DIYLC-notarize.zip)
|
||||
while IFS= read -r -d '' zipfile; do
|
||||
filename=$(basename "${zipfile}")
|
||||
# Skip DIYLC-notarize.zip files
|
||||
if [[ "${filename}" != "DIYLC-notarize.zip" ]] && [ -f "${zipfile}" ]; then
|
||||
dest="${DEPLOY_DIR}/${filename}"
|
||||
|
||||
echo " → Copying: ${filename}"
|
||||
cp "${zipfile}" "${dest}"
|
||||
TOTAL_ZIP_COUNT=$((TOTAL_ZIP_COUNT + 1))
|
||||
PROFILE_FILE_COUNT=$((PROFILE_FILE_COUNT + 1))
|
||||
fi
|
||||
done < <(find "${TARGET_DIR}" -maxdepth 1 -type f -name "*.zip" -print0 2>/dev/null)
|
||||
|
||||
# Find all exe files in target directory
|
||||
while IFS= read -r -d '' exefile; do
|
||||
if [ -f "${exefile}" ]; then
|
||||
filename=$(basename "${exefile}")
|
||||
dest="${DEPLOY_DIR}/${filename}"
|
||||
|
||||
echo " → Copying: ${filename}"
|
||||
cp "${exefile}" "${dest}"
|
||||
TOTAL_ZIP_COUNT=$((TOTAL_ZIP_COUNT + 1))
|
||||
PROFILE_FILE_COUNT=$((PROFILE_FILE_COUNT + 1))
|
||||
fi
|
||||
done < <(find "${TARGET_DIR}" -maxdepth 1 -type f -name "*.exe" -print0 2>/dev/null)
|
||||
|
||||
if [ ${PROFILE_FILE_COUNT} -eq 0 ]; then
|
||||
echo " ⚠ Warning: No artifacts found for profile ${profile}"
|
||||
# List what's actually in the target directory for debugging
|
||||
echo " Debug: Files in target directory:"
|
||||
ls -la "${TARGET_DIR}"/*.{zip,exe} 2>/dev/null | head -5 || echo " (no zip/exe files found)"
|
||||
else
|
||||
echo " ✓ Copied ${PROFILE_FILE_COUNT} file(s) from ${profile}"
|
||||
fi
|
||||
else
|
||||
echo " ⚠ Warning: Target directory ${TARGET_DIR} does not exist"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=========================================="
|
||||
echo "Copying summary"
|
||||
echo "=========================================="
|
||||
if [ ${TOTAL_ZIP_COUNT} -eq 0 ]; then
|
||||
echo "No artifacts were copied"
|
||||
else
|
||||
echo "✓ Copied ${TOTAL_ZIP_COUNT} file(s) total to ${DEPLOY_DIR}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Build and deployment complete!"
|
||||
echo "=========================================="
|
||||
echo "Deployment folder: ${DEPLOY_DIR}"
|
||||
echo "Files:"
|
||||
ls -lh "${DEPLOY_DIR}"/*.{zip,exe} 2>/dev/null || echo " (no zip/exe files found)"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-core</artifactId>
|
||||
|
||||
@@ -68,5 +68,9 @@ public enum DrawOption {
|
||||
/**
|
||||
* Whether locked components should be rendered as transparent or not.
|
||||
*/
|
||||
LOCKED_ALPHA;
|
||||
LOCKED_ALPHA,
|
||||
/**
|
||||
* When applied, all colors will be converted to blackest black or whitest white
|
||||
*/
|
||||
MONOCHROMATIC;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
package org.diylc.common;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.diylc.core.Project;
|
||||
@@ -55,12 +54,7 @@ public enum EventType {
|
||||
*/
|
||||
FILE_STATUS_CHANGED,
|
||||
/**
|
||||
* Called when selection rectangle is changed. Object of type {@link Rectangle} is the only
|
||||
* parameter and it contains the new selection rectangle.
|
||||
*/
|
||||
SELECTION_RECT_CHANGED,
|
||||
/**
|
||||
* Called when component selection is changed. New {@link ComponentSelection} is attached as a
|
||||
* Called when component selection is changed. New list of components and control point map is attached as a
|
||||
* parameter.
|
||||
*/
|
||||
SELECTION_CHANGED,
|
||||
|
||||
@@ -41,16 +41,18 @@ import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Template;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.filter.IComponentFilter;
|
||||
import org.diylc.test.ITestProcessor;
|
||||
|
||||
/**
|
||||
* Interface for communication between plug-ins and the application. Plug-ins may acquire
|
||||
* information or make changes through this interface. Application events are dispatched to plug-ins
|
||||
* through {@link MessageDispatcher}
|
||||
*
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*
|
||||
*
|
||||
* @see IPlugIn
|
||||
* @see MessageDispatcher
|
||||
* @see IMessageListener
|
||||
@@ -84,18 +86,20 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
public static final String RENUMBER_ON_PASTE_KEY = "renumberOnPaste";
|
||||
public static final String RULER_IN_SUBDIVISION_KEY = "rulerInSubdivision";
|
||||
public static final String CACHING_ENABLED_KEY = "cachingEnabled";
|
||||
|
||||
public static final String FONT_SCALE_FACTOR_KEY = "fontScaleFactor";
|
||||
public static final String SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY = "showResizeDimensionsTooltip";
|
||||
|
||||
public static final String RULER_IN_SUBDIVISION_2 = "base of 2";
|
||||
public static final String RULER_IN_SUBDIVISION_10 = "base of 10";
|
||||
public static final String RULER_IN_SUBDIVISION_DEFAULT = RULER_IN_SUBDIVISION_10;
|
||||
|
||||
|
||||
public static final String SNAP_TO_NONE = "None";
|
||||
public static final String SNAP_TO_GRID = "Grid";
|
||||
public static final String SNAP_TO_COMPONENTS = "Components";
|
||||
public static final String SNAP_TO_DEFAULT = SNAP_TO_GRID;
|
||||
|
||||
|
||||
public static final String LOCKED_ALPHA = "lockedAlpha";
|
||||
|
||||
|
||||
public static final String LANGUAGE = "language";
|
||||
public static final String LANGUAGE_DEFAULT = "English";
|
||||
|
||||
@@ -108,25 +112,25 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
public static final int NOBUTTON = 0;
|
||||
public static final int BUTTON1 = 1;
|
||||
public static final int BUTTON2 = 2;
|
||||
public static final int BUTTON3 = 3;
|
||||
public static final int BUTTON3 = 3;
|
||||
|
||||
/**
|
||||
* Returns size of the canvas that takes project dimensions into account as well as zoom level.
|
||||
* Each dimension is calculated as the product of the actual size and the number of pixels per
|
||||
* unit. If <code>useZoom</code> is set to true, the result is scaled by zoom factor.
|
||||
*
|
||||
*
|
||||
* @param useZoom
|
||||
* @param includeExtraSpace
|
||||
*
|
||||
*
|
||||
* @return canvas dimensions
|
||||
*/
|
||||
Dimension getCanvasDimensions(boolean useZoom, boolean includeExtraSpace);
|
||||
|
||||
/**
|
||||
* Returns an instance of {@link Cursor} that should be used at the specified location.
|
||||
*
|
||||
*
|
||||
* Note: point coordinates are scaled for zoom factor.
|
||||
*
|
||||
*
|
||||
* @param point
|
||||
* @param ctrlDown
|
||||
* @param shiftDown
|
||||
@@ -137,14 +141,14 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Returns an instance of currently loaded project.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Project getCurrentProject();
|
||||
|
||||
/**
|
||||
* Loads specified {@link Project}.
|
||||
*
|
||||
*
|
||||
* @param project
|
||||
* @param freshStart
|
||||
* @param filename file name when loading from a file
|
||||
@@ -158,22 +162,22 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Loads a project from the specified file.
|
||||
*
|
||||
*
|
||||
* @param fileName
|
||||
*/
|
||||
void loadProjectFromFile(String fileName);
|
||||
|
||||
/**
|
||||
* Saves the current project into the specified file.
|
||||
*
|
||||
*
|
||||
* @param fileName
|
||||
* @param isBackup
|
||||
*/
|
||||
void saveProjectToFile(String fileName, boolean isBackup);
|
||||
|
||||
|
||||
/**
|
||||
* Exports the current project into one or more files - one per layer and potentially drill layer.
|
||||
*
|
||||
*
|
||||
* @param fileNameBase
|
||||
* @param g2d
|
||||
*/
|
||||
@@ -191,7 +195,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Shows a user dialog if there are changes to confirm that it's safe to proceed.
|
||||
*
|
||||
*
|
||||
* @return true, if file actions (new, open, close) can be taken
|
||||
*/
|
||||
boolean allowFileAction();
|
||||
@@ -200,7 +204,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
* Returns all available {@link ComponentType}s classified by category. Result is a {@link Map}
|
||||
* between category name to a {@link List} of all {@link ComponentType}s that share that category
|
||||
* name.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<String, List<ComponentType>> getComponentTypes();
|
||||
@@ -208,7 +212,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
/**
|
||||
* Draws project on the provided {@link Graphics2D}. If the provided filter is not null, it will
|
||||
* be used to filter the components that are shown.
|
||||
*
|
||||
*
|
||||
* @param g2d
|
||||
* @param drawOptions specific drawing options
|
||||
* @param filter
|
||||
@@ -222,35 +226,35 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Returns current zoom level where <code>zoomLevel = 1.0d</code> means 100%.
|
||||
*
|
||||
*
|
||||
* @return current zoom level
|
||||
*/
|
||||
double getZoomLevel();
|
||||
|
||||
/**
|
||||
* Changes current zoom level where <code>zoomLevel = 1.0d</code> means 100%
|
||||
*
|
||||
*
|
||||
* @param zoomLevel new zoom leve
|
||||
*/
|
||||
void setZoomLevel(double zoomLevel);
|
||||
|
||||
/**
|
||||
* Returns current version number.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
VersionNumber getCurrentVersionNumber();
|
||||
|
||||
|
||||
/**
|
||||
* Returns few most recent versions.
|
||||
*
|
||||
* Returns few most recent versions.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<Version> getRecentUpdates();
|
||||
|
||||
/**
|
||||
* Adds a list of components to the project.
|
||||
*
|
||||
*
|
||||
* @param componentTransferable
|
||||
* @param autoGroup
|
||||
* @param assignNewNames
|
||||
@@ -269,7 +273,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Sets default value for the specified property name for currently selected component types.
|
||||
*
|
||||
*
|
||||
* @param propertyName display name for property
|
||||
* @param value new default value, must not be null
|
||||
*/
|
||||
@@ -277,7 +281,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Sets default value for the specified property name for projects.
|
||||
*
|
||||
*
|
||||
* @param clazz class to set defaults to
|
||||
* @param propertyName display name for property
|
||||
* @param value new default value, must not be null
|
||||
@@ -291,6 +295,15 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
*/
|
||||
List<PropertyWrapper> getMutualSelectionProperties();
|
||||
|
||||
/**
|
||||
* Available only during resizing, returns the dimensions of the component being resized.
|
||||
* Otherwise returns null.
|
||||
*
|
||||
* @return ResizeDimensions with width, height (in mm or in depending on configuration),
|
||||
* and length (if component implements IHaveLength), or null if not resizing
|
||||
*/
|
||||
ResizeDimensions getResizeDimensions();
|
||||
|
||||
void editSelection();
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,16 +37,14 @@ import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.LineStyle;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComponent<T> {
|
||||
public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComponent<T> implements
|
||||
IHaveLength {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -337,8 +335,8 @@ public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComp
|
||||
// }
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Size getLength() {
|
||||
@EditableProperty(name = "Length")
|
||||
public Size calculateLength() {
|
||||
double d = 0;
|
||||
try {
|
||||
Point2D[] p = getControlPoints();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
public interface IHaveLength {
|
||||
|
||||
Size calculateLength();
|
||||
}
|
||||
@@ -300,7 +300,7 @@ public class GerberG2DWrapper extends Graphics2D
|
||||
|
||||
@Override
|
||||
public AffineTransform getTransform() {
|
||||
return tx;
|
||||
return new AffineTransform(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Record containing resize dimensions for a component being resized.
|
||||
*
|
||||
* @param width the width of the component (in mm or in, depending on configuration)
|
||||
* @param height the height of the component (in mm or in, depending on configuration)
|
||||
* @param length the length of the component (if it implements IHaveLength), null otherwise
|
||||
*/
|
||||
public record ResizeDimensions(Size width, Size height, Size length) {
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.common;
|
||||
package org.diylc.filter;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
+1
-2
@@ -19,11 +19,10 @@
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
package org.diylc.filter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.diylc.common.IComponentFilter;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.diylc.filter;
|
||||
|
||||
import org.diylc.core.IBoard;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.gerber.GerberRenderMode;
|
||||
import org.diylc.core.gerber.IGerberComponent;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class SilkScreenFilter implements IComponentFilter {
|
||||
|
||||
@Override
|
||||
public boolean testComponent(IDIYComponent<?> component) {
|
||||
if (component instanceof IBoard board) {
|
||||
return board.shouldExportToGerber();
|
||||
}
|
||||
if (!(component instanceof IGerberComponent gerberComponent)) {
|
||||
return false;
|
||||
}
|
||||
Set<GerberRenderMode> gerberRenderModes = gerberComponent.getGerberRenderModes();
|
||||
return gerberRenderModes.contains(GerberRenderMode.Outline);
|
||||
}
|
||||
}
|
||||
@@ -101,23 +101,7 @@ public class NetlistBuilder {
|
||||
|
||||
// if there are no switches, make one with 1 position so we get 1 result back
|
||||
if (switches.isEmpty())
|
||||
switches.add(new ISwitch() {
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
switches.add(new TrivialSwitch());
|
||||
|
||||
// construct all possible combinations
|
||||
int[] positions = new int[switches.size()];
|
||||
@@ -418,7 +402,9 @@ public class NetlistBuilder {
|
||||
// handle direct connections or connections through a switch
|
||||
// when not using "includeSwitches" flag
|
||||
if (c instanceof IContinuity &&
|
||||
(!(c instanceof ISwitch) || switchPositions.isEmpty())) {
|
||||
// ugly way to check if running in "exclude switches" mode.
|
||||
(!(c instanceof ISwitch) || (switchPositions.size() == 1 &&
|
||||
switchPositions.keySet().stream().findFirst().get() instanceof TrivialSwitch))) {
|
||||
for (int i = 0; i < c.getControlPointCount() - 1; i++)
|
||||
for (int j = i + 1; j < c.getControlPointCount(); j++)
|
||||
if (((IContinuity) c).arePointsConnected(i, j))
|
||||
@@ -493,4 +479,23 @@ public class NetlistBuilder {
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TrivialSwitch implements ISwitch {
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
@@ -71,4 +72,54 @@ public class CalcUtils {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the relative luminance (perceived brightness) of a color.
|
||||
* Uses the standard formula: L = 0.299*R + 0.587*G + 0.114*B
|
||||
*
|
||||
* @param color the color to calculate luminance for
|
||||
* @return the luminance value between 0 and 255
|
||||
*/
|
||||
public static double calculateLuminance(Color color) {
|
||||
if (color == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return 0.299 * color.getRed() + 0.587 * color.getGreen() + 0.114 * color.getBlue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color to monochrome (black or white) based on its luminance.
|
||||
* Colors with luminance below the threshold (128) are converted to black,
|
||||
* otherwise to white. The alpha channel is preserved if present.
|
||||
*
|
||||
* @param color the color to convert
|
||||
* @param threshold the luminance threshold (default 128 for middle gray)
|
||||
* @return black or white color, preserving alpha channel
|
||||
*/
|
||||
public static Color convertToMonochrome(Color color, double threshold) {
|
||||
if (color == null) {
|
||||
return null;
|
||||
}
|
||||
double luminance = calculateLuminance(color);
|
||||
// Use <= to handle edge case where luminance equals threshold (should be white)
|
||||
// Add small epsilon to account for floating point precision issues
|
||||
Color monochromeColor = (luminance < threshold - 1e-10) ? Color.BLACK : Color.WHITE;
|
||||
// Preserve alpha channel if present
|
||||
if (color.getAlpha() < 255) {
|
||||
monochromeColor = new Color(monochromeColor.getRed(), monochromeColor.getGreen(),
|
||||
monochromeColor.getBlue(), color.getAlpha());
|
||||
}
|
||||
return monochromeColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color to monochrome (black or white) based on its luminance.
|
||||
* Uses the default threshold of 128 (middle gray).
|
||||
*
|
||||
* @param color the color to convert
|
||||
* @return black or white color, preserving alpha channel
|
||||
*/
|
||||
public static Color convertToMonochrome(Color color) {
|
||||
return convertToMonochrome(color, 128.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,12 @@ import java.lang.ref.SoftReference;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.core.ComponentForRender;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
@@ -67,14 +69,14 @@ public class DrawingCache {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void bulkPrepare(Collection<ComponentForRender> components, G2DWrapper g2d, boolean outlineMode, Project project,
|
||||
double zoom, double scaleFactor) {
|
||||
double zoom, double scaleFactor, Set<DrawOption> drawOptions) {
|
||||
// run all renders in parallel to utilize all CPU cores
|
||||
components.stream().parallel().forEach(x -> {
|
||||
ComponentType type = ComponentProcessor.getInstance()
|
||||
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) x.getComponent().getClass());
|
||||
|
||||
if (type != null && type.getEnableCache()) {
|
||||
renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder());
|
||||
renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder(), drawOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -92,7 +94,7 @@ public class DrawingCache {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void draw(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState,
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Rectangle2D visibleRect) {
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Rectangle2D visibleRect, Set<DrawOption> drawOptions) {
|
||||
ComponentType type = ComponentProcessor.getInstance()
|
||||
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
|
||||
|
||||
@@ -100,7 +102,7 @@ public class DrawingCache {
|
||||
// if we need to apply caching
|
||||
Point2D firstPoint = component.getControlPoint(0);
|
||||
|
||||
CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, scaleFactor, zOrder);
|
||||
CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, scaleFactor, zOrder, drawOptions);
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
@@ -180,7 +182,7 @@ public class DrawingCache {
|
||||
}
|
||||
|
||||
private CacheValue renderAndCache(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState,
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder) {
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Set<DrawOption> drawOptions) {
|
||||
|
||||
if (!g2d.getClipBounds().intersects(component.getCachingBounds())) {
|
||||
return null;
|
||||
@@ -199,9 +201,11 @@ public class DrawingCache {
|
||||
}
|
||||
|
||||
// only honor the cache if the component hasn't changed in the meantime
|
||||
// Also check if drawOptions changed (e.g., MONOCHROMATIC affects rendering)
|
||||
boolean drawOptionsChanged = value == null || !value.getDrawOptions().equals(drawOptions);
|
||||
if (value == null || !value.getComponent().equalsTo(component)
|
||||
|| value.getState() != componentState || value.getZoom() != zoom || value.getOutlineMode() != outlineMode
|
||||
|| value.getScaleFactor() != scaleFactor) {
|
||||
|| value.getScaleFactor() != scaleFactor || drawOptionsChanged) {
|
||||
// figure out size and placement of buffer image
|
||||
Rectangle2D rect = component.getCachingBounds();
|
||||
int width = (int)Math.round(rect.getWidth() * zoom);
|
||||
@@ -233,7 +237,7 @@ public class DrawingCache {
|
||||
cg2d.translate(-firstPoint.getX() + dx, -firstPoint.getY() + dy);
|
||||
|
||||
// wrap the graphics so we can track
|
||||
G2DWrapper wrapper = new G2DWrapper(cg2d, zoom);
|
||||
G2DWrapper wrapper = new G2DWrapper(cg2d, zoom, drawOptions);
|
||||
|
||||
// initialize wrapper
|
||||
wrapper.startedDrawingComponent(zOrder);
|
||||
@@ -275,7 +279,7 @@ public class DrawingCache {
|
||||
|
||||
// add to the cache
|
||||
value = new CacheValue(component, image, wrapper.getCurrentArea(), wrapper.getContinuityPositiveAreas(),
|
||||
wrapper.getContinuityNegativeAreas(), componentState, outlineMode, zoom, scaleFactor, dx, dy, rect);
|
||||
wrapper.getContinuityNegativeAreas(), componentState, outlineMode, zoom, scaleFactor, dx, dy, rect, drawOptions);
|
||||
imageCache.put(component, new SoftReference<DrawingCache.CacheValue>(value));
|
||||
}
|
||||
|
||||
@@ -295,11 +299,12 @@ public class DrawingCache {
|
||||
int dx;
|
||||
int dy;
|
||||
Rectangle2D cacheBounds;
|
||||
Set<DrawOption> drawOptions;
|
||||
|
||||
public CacheValue(IDIYComponent<?> component, BufferedImage image, Area currentArea,
|
||||
Map<String, Area> continuityPositiveAreas, Map<String, Area> continuityNegativeAreas,
|
||||
ComponentState state, boolean outlineMode, double zoom, double scaleFactor, int dx, int dy,
|
||||
Rectangle2D cacheBounds) {
|
||||
Rectangle2D cacheBounds, Set<DrawOption> drawOptions) {
|
||||
super();
|
||||
this.cacheBounds = cacheBounds;
|
||||
// take a copy of the component, so we can check if it changed in the meantime
|
||||
@@ -317,6 +322,7 @@ public class DrawingCache {
|
||||
this.dx = dx;
|
||||
this.dy = dy;
|
||||
this.scaleFactor = scaleFactor;
|
||||
this.drawOptions = drawOptions;
|
||||
}
|
||||
|
||||
public IDIYComponent<?> getComponent() {
|
||||
@@ -366,6 +372,10 @@ public class DrawingCache {
|
||||
public Rectangle2D getCacheBounds() {
|
||||
return cacheBounds;
|
||||
}
|
||||
|
||||
public Set<DrawOption> getDrawOptions() {
|
||||
return drawOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.filter.IComponentFilter;
|
||||
import org.diylc.netlist.ContinuityGraph;
|
||||
import org.diylc.netlist.NetlistBuilder;
|
||||
import org.diylc.utils.Constants;
|
||||
@@ -183,7 +184,7 @@ public class DrawingManager {
|
||||
if (externalZoom != null)
|
||||
zoom *= externalZoom;
|
||||
|
||||
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
|
||||
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom, drawOptions);
|
||||
|
||||
configureRenderingHints(g2d, drawOptions);
|
||||
|
||||
@@ -267,7 +268,7 @@ public class DrawingManager {
|
||||
|
||||
// give the cache a chance to prepare all the components in multiple threads
|
||||
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) {
|
||||
DrawingCache.Instance.bulkPrepare(componentForRenderList, g2dWrapper, outlineMode, project, zoom, scaleFactor);
|
||||
DrawingCache.Instance.bulkPrepare(componentForRenderList, g2dWrapper, outlineMode, project, zoom, scaleFactor, drawOptions);
|
||||
}
|
||||
|
||||
// componentAreaMap.clear();
|
||||
@@ -561,10 +562,12 @@ public class DrawingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor, Rectangle2D visibleRect, IDIYComponent<?> component, G2DWrapper g2dWrapper, ComponentState state, double zoom, int i, boolean outlineMode) {
|
||||
private static void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor,
|
||||
Rectangle2D visibleRect, IDIYComponent<?> component, G2DWrapper g2dWrapper, ComponentState state,
|
||||
double zoom, int i, boolean outlineMode) {
|
||||
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
|
||||
DrawingCache.Instance.draw(component, g2dWrapper, state,
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect);
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect, drawOptions);
|
||||
else // go straight to the wrapper
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
}
|
||||
|
||||
@@ -53,17 +53,11 @@ import java.awt.image.renderable.RenderableImage;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.ZoomableStroke;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -71,7 +65,7 @@ import org.diylc.core.IDrawingObserver;
|
||||
/**
|
||||
* {@link Graphics2D} wrapper that keeps track of all drawing actions and creates an {@link Area}
|
||||
* that corresponds to drawn objects. Before each component is drawn,
|
||||
* {@link #startedDrawingComponent()} should be called. After the component is drawn, area may be
|
||||
* {@link #startedDrawingComponent(int)} should be called. After the component is drawn, area may be
|
||||
* retrieved using {@link #finishedDrawingComponent()}. Graphics configuration (color, font, etc) is
|
||||
* reset between each two components.
|
||||
*
|
||||
@@ -96,7 +90,9 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
private int uniqueCounter = 0;
|
||||
|
||||
private Graphics2D canvasGraphics;
|
||||
private final Graphics2D canvasGraphics;
|
||||
private final Set<DrawOption> drawOptions;
|
||||
|
||||
private Stroke originalStroke;
|
||||
private Color originalColor;
|
||||
private Composite originalComposite;
|
||||
@@ -115,12 +111,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
/**
|
||||
* Creates a wrapper around specified {@link Graphics2D} object.
|
||||
*
|
||||
*
|
||||
* @param canvasGraphics
|
||||
* @param drawOptions
|
||||
*/
|
||||
public G2DWrapper(Graphics2D canvasGraphics, double zoom) {
|
||||
public G2DWrapper(Graphics2D canvasGraphics, double zoom, Set<DrawOption> drawOptions) {
|
||||
super();
|
||||
this.canvasGraphics = canvasGraphics;
|
||||
this.drawOptions = drawOptions;
|
||||
String debugAreaPerformanceStr = System.getProperty(DEBUG_AREA_PERFORMANCE);
|
||||
debugAreaPerformance = "true".equalsIgnoreCase(debugAreaPerformanceStr);
|
||||
this.zoom = zoom;
|
||||
@@ -482,7 +480,9 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public void setComposite(Composite comp) {
|
||||
canvasGraphics.setComposite(comp);
|
||||
if (!drawOptions.contains(DrawOption.OUTLINE_MODE)) {
|
||||
canvasGraphics.setComposite(comp);
|
||||
}
|
||||
// FIXME: check this.
|
||||
}
|
||||
|
||||
@@ -758,7 +758,11 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public void setColor(Color c) {
|
||||
canvasGraphics.setColor(c);
|
||||
if (drawOptions.contains(DrawOption.MONOCHROMATIC) && c != null) {
|
||||
canvasGraphics.setColor(CalcUtils.convertToMonochrome(c));
|
||||
} else {
|
||||
canvasGraphics.setColor(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -59,18 +59,13 @@ import com.thoughtworks.xstream.security.AnyTypePermission;
|
||||
|
||||
import org.diylc.clipboard.ComponentTransferable;
|
||||
import org.diylc.common.*;
|
||||
import org.diylc.core.ExpansionMode;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDatasheetSupport;
|
||||
import org.diylc.core.IView;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Template;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.IAutoCreator;
|
||||
import org.diylc.core.gerber.GerberExporter;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.filter.IComponentFilter;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.netlist.INetlistParser;
|
||||
import org.diylc.netlist.Netlist;
|
||||
@@ -250,8 +245,10 @@ public class Presenter implements IPlugInPort {
|
||||
@Override
|
||||
public Cursor getCursorAt(Point point, boolean ctrlDown, boolean shiftDown, boolean altDown) {
|
||||
// Only change the cursor if we're not making a new component.
|
||||
if (operationMode == OperationMode.HIGHLIGHT_CONNECTED_AREAS || altDown)
|
||||
if (operationMode == OperationMode.HIGHLIGHT_CONNECTED_AREAS || altDown) {
|
||||
return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
|
||||
}
|
||||
|
||||
if (instantiationManager.getComponentTypeSlot() == null) {
|
||||
// Scale point to remove zoom factor.
|
||||
Point2D scaledPoint = scalePoint(point);
|
||||
@@ -268,6 +265,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Cursor.getDefaultCursor();
|
||||
}
|
||||
|
||||
@@ -427,7 +425,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, Set<DrawOption> drawOptions, final IComponentFilter filter, Double externalZoom,
|
||||
public void draw(Graphics2D g2d, Set<DrawOption> drawOptions, final IComponentFilter filter, Double externalZoom,
|
||||
Double scaleFactor, Rectangle2D visibleRect) {
|
||||
if (currentProject == null) {
|
||||
return;
|
||||
@@ -924,29 +922,36 @@ public class Presenter implements IPlugInPort {
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getSelectionBounds(boolean applyZoom) {
|
||||
if (selectedComponents == null || selectedComponents.isEmpty())
|
||||
return getComponentBounds(selectedComponents, applyZoom);
|
||||
}
|
||||
|
||||
private Rectangle2D getComponentBounds(Collection<IDIYComponent<?>> components, boolean applyZoom) {
|
||||
if (components == null || components.isEmpty())
|
||||
return null;
|
||||
|
||||
int minX = Integer.MAX_VALUE;
|
||||
int maxX = Integer.MIN_VALUE;
|
||||
int minY = Integer.MAX_VALUE;
|
||||
int maxY = Integer.MIN_VALUE;
|
||||
for (IDIYComponent<?> c : selectedComponents) {
|
||||
|
||||
for (IDIYComponent<?> c : components) {
|
||||
|
||||
ComponentArea compArea = drawingManager.getComponentArea(c);
|
||||
if (compArea != null && compArea.getOutlineArea() != null) {
|
||||
Rectangle rect = compArea.getOutlineArea().getBounds();
|
||||
if (rect.x < minX)
|
||||
minX = rect.x;
|
||||
if (rect.x + rect.width > maxX)
|
||||
maxX = rect.x + rect.width;
|
||||
if (rect.y < minY)
|
||||
minY = rect.y;
|
||||
if (rect.y + rect.height > maxY)
|
||||
maxY = rect.y + rect.height;
|
||||
} else if (currentProject.getComponents().contains(c))
|
||||
if (compArea == null || compArea.getOutlineArea() == null) {
|
||||
LOG.debug("Area is null for " + c.getName() + " of type " + c.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle rect = compArea.getOutlineArea().getBounds();
|
||||
if (rect.x < minX)
|
||||
minX = rect.x;
|
||||
if (rect.x + rect.width > maxX)
|
||||
maxX = rect.x + rect.width;
|
||||
if (rect.y < minY)
|
||||
minY = rect.y;
|
||||
if (rect.y + rect.height > maxY)
|
||||
maxY = rect.y + rect.height;
|
||||
}
|
||||
|
||||
if (configManager.readBoolean(EXTRA_SPACE_KEY, true)) {
|
||||
@@ -1015,8 +1020,9 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
updateSelection(matching);
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT, true);
|
||||
if (matching.size() > 0)
|
||||
if (!matching.isEmpty()) {
|
||||
messageDispatcher.dispatchMessage(EventType.SCROLL_TO, getSelectionBounds(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1233,11 +1239,14 @@ public class Presenter implements IPlugInPort {
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("point", point);
|
||||
test.addStep(DIYTest.DRAG_OVER, params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (point == null || operationMode == OperationMode.HIGHLIGHT_CONNECTED_AREAS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean resizeInProgress = false;
|
||||
|
||||
Point2D scaledPoint = scalePoint(point);
|
||||
if (controlPointMap != null && !controlPointMap.isEmpty()) {
|
||||
// We're dragging control point(s).
|
||||
@@ -1248,6 +1257,13 @@ public class Presenter implements IPlugInPort {
|
||||
if (actualD == null)
|
||||
return true;
|
||||
|
||||
// if there's at least one component with only one control point being dragged it means that we are resizing
|
||||
if (controlPointMap.size() == 1 && controlPointMap.entrySet().stream()
|
||||
.anyMatch(entry -> entry.getValue().size() == 1
|
||||
&& entry.getKey().getControlPointCount() > 1)) {
|
||||
resizeInProgress = true;
|
||||
}
|
||||
|
||||
previousDragPoint.setLocation(previousDragPoint.getX() + actualD.getX(), previousDragPoint.getY() + actualD.getY());
|
||||
} else if (selectedComponents.isEmpty() && instantiationManager.getComponentTypeSlot() == null
|
||||
&& previousDragPoint != null) {
|
||||
@@ -1265,10 +1281,50 @@ public class Presenter implements IPlugInPort {
|
||||
this.previousScaledPoint = scalePoint(point);
|
||||
instantiationManager.updateSingleClick(previousScaledPoint, isSnapToGrid(), currentProject.getGridSpacing());
|
||||
}
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT, false, resizeInProgress);
|
||||
return true;
|
||||
}
|
||||
|
||||
public ResizeDimensions getResizeDimensions() {
|
||||
|
||||
if (controlPointMap == null || controlPointMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the component being resized (one with a single control point being dragged)
|
||||
IDIYComponent<?> resizingComponent = null;
|
||||
for (Map.Entry<IDIYComponent<?>, Set<Integer>> entry : controlPointMap.entrySet()) {
|
||||
if (entry.getValue().size() == 1 && entry.getKey().getControlPointCount() > 1) {
|
||||
resizingComponent = entry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resizingComponent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle2D componentBounds = getComponentBounds(controlPointMap.keySet(), false);
|
||||
if (componentBounds == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert pixel dimensions to Size (mm or in based on configuration)
|
||||
boolean metric = configManager.readBoolean(METRIC_KEY, true);
|
||||
SizeUnit unit = metric ? SizeUnit.mm : SizeUnit.in;
|
||||
Size width = Size.fromPixels(componentBounds.getWidth(), unit);
|
||||
Size height = Size.fromPixels(componentBounds.getHeight(), unit);
|
||||
|
||||
// Get length if component implements IHaveLength
|
||||
Size length = null;
|
||||
if (resizingComponent instanceof IHaveLength iHaveLength) {
|
||||
length = iHaveLength.calculateLength();
|
||||
}
|
||||
|
||||
return new ResizeDimensions(width, height, length);
|
||||
}
|
||||
|
||||
private Point2D moveComponents(Map<IDIYComponent<?>, Set<Integer>> controlPointMap, int dx, int dy, boolean snapToGrid, boolean snapToObjects) {
|
||||
// After we make the transfer and snap to grid, calculate actual dx
|
||||
// and dy. We'll use them to translate the previous drag point.
|
||||
@@ -1580,6 +1636,7 @@ public class Presenter implements IPlugInPort {
|
||||
drawingManager.clearContinuityArea();
|
||||
projectFileManager.notifyFileChange();
|
||||
}
|
||||
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
dragInProgress = false;
|
||||
}
|
||||
@@ -2166,6 +2223,10 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
Rectangle2D rect = getSelectionBounds(false);
|
||||
|
||||
if (rect == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double width = rect.getWidth();
|
||||
double height = rect.getHeight();
|
||||
|
||||
@@ -2508,7 +2569,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
try {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(
|
||||
"import-defaults/variants.xml"));
|
||||
"pending-variants.xml"));
|
||||
XStream xStream = new XStream(new DomDriver());
|
||||
xStream.addPermission(AnyTypePermission.ANY);
|
||||
ProjectFileManager.xStreamSerializer.toXML(defaultVariantMap, out);
|
||||
|
||||
@@ -4128,4 +4128,204 @@
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
</map>
|
||||
<entry>
|
||||
<string>org.diylc.components.electromechanical.TactileMicroSwitch</string>
|
||||
<list>
|
||||
<org.diylc.core.Template>
|
||||
<name>6x6 (e.g. Omron B3F-1000)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="6.5" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="4.5" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="6.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW2</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Opposite</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.175"/>
|
||||
<point x="0.255" y="0.0"/>
|
||||
<point x="0.255" y="0.175"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:01:17.805688</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>12x12 (e.g. Omron B3F-4000)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="12.6" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="5.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="12.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW1</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Opposite</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.195"/>
|
||||
<point x="0.495" y="0.0"/>
|
||||
<point x="0.495" y="0.195"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:00:31.950676</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>5x5 Low Profile SMD (e.g. Alps SKQG)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="5.2" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="3.7" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="5.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW2</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Diagonal</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.145"/>
|
||||
<point x="0.2" y="0.0"/>
|
||||
<point x="0.2" y="0.145"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:04:33.795741</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
</map>
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
<minor>5</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-05-13 00:00:00.000 CET</releaseDate>
|
||||
<releaseDate>2025-05-26 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
@@ -313,6 +313,10 @@
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>X or Q keys sometimes do not register in secondary dialogs</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>'Auto-Edit Mode' doesn't do what it's supposed to</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>If the internet connection is present, but unstable, the application starts but nothing can be clicked until the connection restores</description>
|
||||
@@ -320,5 +324,225 @@
|
||||
</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>6</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-08-11 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Drill Hole' component that can be used for board mounting holes, chassis holes, faceplace holes, etc</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Simplify zoom in/out keyboard shortcuts to only plus/minus keys and zero numerical key for zoom reset to 100</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Bundle Java JRE with Linux AppImage to avoid having to install it</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Updated naming conventions Mac build packages to replace 'arm' with 'silicon' and 'x86' to 'intel' for clarity</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Allow Windows 64 bit installer to run on ARM-based Windows machine (through emulation)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>More intuitive user account creation dialog for DIYLC Cloud - showing exact requirements for password and marking optional entries</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved project search algorithm in DIYLC cloud</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>When a switch position is selected (i.e. set to a value other than None), DIYLC will treat the terminals that are internally connected in that switching logic as connected in analysis tools</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Increase maximum size of the backup folder from 64MB to 1GB for deeper history</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Alignment issues of some visual elements in the resulting Gerber files, e.g. DIL IC pin designators</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>'Connect to Patreon' option says that it connected account with username 'a b c' but doesn't do anything</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>
|
||||
<minor>7</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-12-21 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added '6P5T (5-Position 6 Pole AxLabs)' lever switch type (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added '4P3T (3-Position 4 Pole AxLabs)' lever switch type (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'DP3T (Import 5-Position 2502N)' lever switch type (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Corrected 'S1 Switch' up/down position designators (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Corrected 'Mini Toggle Switch' position names for center-OFF variants to be in the correct order (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Drill Hole' changes color to red when selected</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>
|
||||
<minor>8</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-12-30 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added Silkscreen export to PDF/PNG and print capabilities</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Multiwatt IC' package with 11 and 15 pin varieties</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added new property in 'LP Switch' that allows having the middle terminals connected (as they were) or disconnected to allow more flexibility</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Ending Style' property to 'Copper Trace' and 'Curved Trace' to allow choosing round or flat style</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added Chinese simplified (thank you chenjingyuanku) and Hindi UI translation and updated all other languages with new translations</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added configuration for text scale factor in rare cases when system scale factor is not applied correctly; requires adding a configuration property to config.xml with key 'fontScaleFactor' and a desired float value</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved behavior of mouse cursor during drag operations; this should also fix behavior on some linux machines that show a "forbidden" cursor while dragging</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved sensitivity of 'Multimeter Probe' that required pixel-level precision to detect connectivity; improved rendering of the probe in outline mode</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improve contrast of tooltips on some Linux machines that show white text against yellow balloon</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Honor label color settings for 'Tape Measure' component</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Reorganized export and print related items in the File menu to reduce clutter</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Fixed underside rendering and mirroring position issues that affected only SPDT 'Mini Toggle Switch' types</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Application freezes when using underside rendering or mirroring on 'Pin Header' when sized at 1x1</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>'Rotary Switch (Open)' cannot be selected by clicking on the wafer for 4-pole variations</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>
|
||||
<minor>9</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-12-30 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added balloon tooltip next to cursor showing component width, height and length (where available) while resizing; can be disabled from Config menu</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Freeway 3x3-05 (Ultra) Switch' (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Configurable 'Tactile Micro Switch' dimensions and added a few most commonly used variants</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Exposed the most common pin pairing modes and added switching logic for 'Tactile Micro Switch' to allow using it for analysis</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>'Potentiometer Panel' can now render numerical lug markers for easier identification</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Auto-Wrap Label' retains line breaks from the input text</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Cliff 1/8" Jack', 'Open 1/8" Jack' and 'Open 1/4" Jack' can be rotated</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Fixed the offset of terminals on a DP4T Tele type of 'Lever Switch' to be aligned with the physical switch (attention - this will break existing diagrams and will require adjusting leads to match the new terminal positions)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Make the label of schematic symbols (e.g. 'Resistor Symbol') clickable as well</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Ability to resize 'Transformer Core' schematic symbol after it's already placed</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Clicking on 'Tactile Micro Switch' body doesn't register</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>New lines at the end of the 'Label' text get erroneously rendered as br HTML tags</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>
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.diylc.presenter;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -142,4 +143,116 @@ public class CalcUtilsTest {
|
||||
point2.setLocation(10.0, 21.0);
|
||||
assertFalse(CalcUtils.pointsMatch(point1, point2, 0.5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateLuminance() {
|
||||
// Test pure black
|
||||
assertEquals(0.0, CalcUtils.calculateLuminance(Color.BLACK), 0.001);
|
||||
|
||||
// Test pure white
|
||||
assertEquals(255.0, CalcUtils.calculateLuminance(Color.WHITE), 0.001);
|
||||
|
||||
// Test pure red
|
||||
double redLuminance = CalcUtils.calculateLuminance(Color.RED);
|
||||
assertEquals(76.245, redLuminance, 0.001); // 0.299 * 255
|
||||
|
||||
// Test pure green
|
||||
double greenLuminance = CalcUtils.calculateLuminance(Color.GREEN);
|
||||
assertEquals(149.685, greenLuminance, 0.001); // 0.587 * 255
|
||||
|
||||
// Test pure blue
|
||||
double blueLuminance = CalcUtils.calculateLuminance(Color.BLUE);
|
||||
assertEquals(29.07, blueLuminance, 0.001); // 0.114 * 255
|
||||
|
||||
// Test middle gray (128, 128, 128)
|
||||
Color gray = new Color(128, 128, 128);
|
||||
double grayLuminance = CalcUtils.calculateLuminance(gray);
|
||||
assertEquals(128.0, grayLuminance, 0.001);
|
||||
|
||||
// Test null color
|
||||
assertEquals(0.0, CalcUtils.calculateLuminance(null), 0.001);
|
||||
|
||||
// Test custom color
|
||||
Color custom = new Color(100, 150, 200);
|
||||
double customLuminance = CalcUtils.calculateLuminance(custom);
|
||||
double expected = 0.299 * 100 + 0.587 * 150 + 0.114 * 200;
|
||||
assertEquals(expected, customLuminance, 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMonochrome() {
|
||||
// Test pure black -> should stay black
|
||||
Color result = CalcUtils.convertToMonochrome(Color.BLACK);
|
||||
assertEquals(Color.BLACK, result);
|
||||
|
||||
// Test pure white -> should stay white
|
||||
result = CalcUtils.convertToMonochrome(Color.WHITE);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test dark color -> should become black
|
||||
Color darkBlue = new Color(0, 0, 50);
|
||||
result = CalcUtils.convertToMonochrome(darkBlue);
|
||||
assertEquals(Color.BLACK, result);
|
||||
|
||||
// Test light color -> should become white
|
||||
Color lightYellow = new Color(255, 255, 200);
|
||||
result = CalcUtils.convertToMonochrome(lightYellow);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test middle gray -> should become white (threshold is 128, gray is exactly 128)
|
||||
Color gray = new Color(128, 128, 128);
|
||||
result = CalcUtils.convertToMonochrome(gray);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test color just below threshold -> should become black
|
||||
Color darkGray = new Color(127, 127, 127);
|
||||
result = CalcUtils.convertToMonochrome(darkGray);
|
||||
assertEquals(Color.BLACK, result);
|
||||
|
||||
// Test color just above threshold -> should become white
|
||||
Color lightGray = new Color(129, 129, 129);
|
||||
result = CalcUtils.convertToMonochrome(lightGray);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test with custom threshold
|
||||
Color mediumColor = new Color(100, 100, 100);
|
||||
result = CalcUtils.convertToMonochrome(mediumColor, 50.0);
|
||||
assertEquals(Color.WHITE, result); // Luminance ~100, threshold 50, so white
|
||||
|
||||
result = CalcUtils.convertToMonochrome(mediumColor, 150.0);
|
||||
assertEquals(Color.BLACK, result); // Luminance ~100, threshold 150, so black
|
||||
|
||||
// Test null color
|
||||
assertNull(CalcUtils.convertToMonochrome(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMonochromeWithAlpha() {
|
||||
// Test color with alpha channel - alpha should be preserved
|
||||
// Red (255, 0, 0) has luminance ~76, which is < 128, so it becomes BLACK
|
||||
Color transparentRed = new Color(255, 0, 0, 128);
|
||||
Color result = CalcUtils.convertToMonochrome(transparentRed);
|
||||
assertEquals(Color.BLACK.getRed(), result.getRed());
|
||||
assertEquals(Color.BLACK.getGreen(), result.getGreen());
|
||||
assertEquals(Color.BLACK.getBlue(), result.getBlue());
|
||||
assertEquals(128, result.getAlpha());
|
||||
|
||||
// Test light color with alpha - should become WHITE
|
||||
Color transparentLight = new Color(255, 255, 200, 128);
|
||||
result = CalcUtils.convertToMonochrome(transparentLight);
|
||||
assertEquals(Color.WHITE.getRed(), result.getRed());
|
||||
assertEquals(Color.WHITE.getGreen(), result.getGreen());
|
||||
assertEquals(Color.WHITE.getBlue(), result.getBlue());
|
||||
assertEquals(128, result.getAlpha());
|
||||
|
||||
// Test fully transparent color
|
||||
Color transparent = new Color(100, 150, 200, 0);
|
||||
result = CalcUtils.convertToMonochrome(transparent);
|
||||
assertEquals(0, result.getAlpha());
|
||||
|
||||
// Test opaque color - alpha should remain 255
|
||||
Color opaque = new Color(50, 50, 50);
|
||||
result = CalcUtils.convertToMonochrome(opaque);
|
||||
assertEquals(255, result.getAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-library</artifactId>
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
@@ -72,13 +71,18 @@ public class StringUtils {
|
||||
* @return a non-empty list of strings
|
||||
*/
|
||||
public static List<String> wrap(String str, FontMetrics fm, int maxWidth) {
|
||||
String[] split = str.split("\\<br\\>");
|
||||
List<String> lines = Arrays.stream(split).flatMap(s -> splitIntoLines(s).stream()).collect(Collectors.toList());
|
||||
if (lines.size() == 0)
|
||||
return lines;
|
||||
// First split by actual newlines to honor them, then split by <br> tags
|
||||
List<String> lines = splitIntoLines(str);
|
||||
List<String> allLines = new ArrayList<String>();
|
||||
for (String line : lines) {
|
||||
String[] split = line.split("\\<br\\>");
|
||||
allLines.addAll(Arrays.asList(split));
|
||||
}
|
||||
if (allLines.size() == 0)
|
||||
return allLines;
|
||||
|
||||
ArrayList<String> strings = new ArrayList<String>();
|
||||
for (Iterator<String> iter = lines.iterator(); iter.hasNext();)
|
||||
for (Iterator<String> iter = allLines.iterator(); iter.hasNext();)
|
||||
wrapLineInto((String) iter.next(), strings, fm, maxWidth);
|
||||
return strings;
|
||||
}
|
||||
@@ -126,8 +130,8 @@ public class StringUtils {
|
||||
line = line.substring(pos).trim();
|
||||
len = line.length();
|
||||
}
|
||||
if (len > 0)
|
||||
list.add(line);
|
||||
// Always add the line, even if empty, to preserve empty lines from newlines
|
||||
list.add(line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,12 +213,24 @@ public class StringUtils {
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
String[] parts = text.split("\\<br\\>");
|
||||
// Use -1 limit to preserve trailing empty strings so we can filter them out
|
||||
String[] parts = text.split("\\<br\\>", -1);
|
||||
if (parts.length > 1) {
|
||||
// Remove trailing empty strings to avoid rendering extra blank lines
|
||||
int lastNonEmpty = parts.length - 1;
|
||||
while (lastNonEmpty >= 0 && parts[lastNonEmpty].trim().isEmpty()) {
|
||||
lastNonEmpty--;
|
||||
}
|
||||
// If all parts are empty, don't render anything
|
||||
if (lastNonEmpty < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics();
|
||||
Rectangle stringBounds = fontMetrics.getStringBounds(parts[0], g2d).getBounds();
|
||||
for (int i = 0; i < parts.length; i++)
|
||||
drawCenteredText(g2d, parts[i], x, (int)(y - stringBounds.height * (parts.length - 1) / 2d + i * stringBounds.height), horizontalAlignment, verticalAlignment);
|
||||
int actualLength = lastNonEmpty + 1;
|
||||
for (int i = 0; i < actualLength; i++)
|
||||
drawCenteredText(g2d, parts[i], x, (int)(y - stringBounds.height * (actualLength - 1) / 2d + i * stringBounds.height), horizontalAlignment, verticalAlignment);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+36
-11
@@ -43,12 +43,7 @@ import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.LineStyle;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractLabeledComponent;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.PositiveNonZeroMeasureValidator;
|
||||
import org.diylc.core.measures.Size;
|
||||
@@ -63,7 +58,8 @@ import org.diylc.utils.Constants;
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponent<T> {
|
||||
public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponent<T> implements
|
||||
IHaveLength {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -367,8 +363,8 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
offset = -offset;
|
||||
}
|
||||
|
||||
drawingObserver.startTracking();
|
||||
if (getMoveLabel()) {
|
||||
drawingObserver.startTracking();
|
||||
g2d.setTransform(oldTransform);
|
||||
g2d.translate(getPoints()[2].getX(), getPoints()[2].getY());
|
||||
if (getLabelOriantation() != LabelOriantation.Horizontal) {
|
||||
@@ -378,19 +374,18 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
g2d.setTransform(oldTransform);
|
||||
} else {
|
||||
if (isStanding() || getLabelOriantation() == LabelOriantation.Horizontal) {
|
||||
drawingObserver.startTracking();
|
||||
g2d.setTransform(oldTransform);
|
||||
double x = (getPoints()[0].getX() + getPoints()[1].getX() - length) / 2.0;
|
||||
double y = (getPoints()[0].getY() + getPoints()[1].getY() - width) / 2.0;
|
||||
g2d.drawString(label, (int) (x + (length - textRect.getWidth()) / 2 + offset),
|
||||
(int) (y + calculateLabelYOffset(shapeRect, textRect, fontMetrics)));
|
||||
} else {
|
||||
drawingObserver.stopTracking();
|
||||
g2d.drawString(label, (int) (length - textRect.getWidth()) / 2 + offset,
|
||||
calculateLabelYOffset(shapeRect, textRect, fontMetrics));
|
||||
g2d.setTransform(oldTransform);
|
||||
}
|
||||
}
|
||||
drawingObserver.stopTracking();
|
||||
}
|
||||
// if (getLabelOriantation() == LabelOriantation.Horizontal) {
|
||||
//
|
||||
@@ -434,7 +429,7 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
Stroke stroke = null;
|
||||
switch (getStyle()) {
|
||||
case SOLID:
|
||||
stroke = ObjectCache.getInstance().fetchBasicStroke(thickness);
|
||||
stroke = ObjectCache.getInstance().fetchStroke(thickness, getEndingStyle() == LineEndingStyle.Round ? BasicStroke.CAP_ROUND : BasicStroke.CAP_BUTT);
|
||||
break;
|
||||
case DASHED:
|
||||
stroke =
|
||||
@@ -641,6 +636,32 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size calculateLength() {
|
||||
|
||||
try {
|
||||
Point2D[] points = getPoints();
|
||||
if (points.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate distance between ending points
|
||||
double distanceInPixels = points[0].distance(points[1]);
|
||||
|
||||
// Convert to Size with appropriate unit (mm or in based on configuration)
|
||||
SizeUnit unit;
|
||||
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.METRIC_KEY, true)) {
|
||||
unit = SizeUnit.mm;
|
||||
} else {
|
||||
unit = SizeUnit.in;
|
||||
}
|
||||
|
||||
return Size.fromPixels(distanceInPixels, unit);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Width", defaultable = true, validatorClass = PositiveNonZeroMeasureValidator.class, sortOrder = 102)
|
||||
public Size getWidth() {
|
||||
return width;
|
||||
@@ -747,6 +768,10 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
hideShortLeads = false;
|
||||
return hideShortLeads;
|
||||
}
|
||||
|
||||
protected LineEndingStyle getEndingStyle() {
|
||||
return LineEndingStyle.Round;
|
||||
}
|
||||
|
||||
public void setHideShortLeads(Boolean hideShortLeads) {
|
||||
this.hideShortLeads = hideShortLeads;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.diylc.components;
|
||||
|
||||
public enum LineEndingStyle {
|
||||
Round, Flat
|
||||
}
|
||||
@@ -41,7 +41,7 @@ import org.diylc.core.annotations.EditableProperty;
|
||||
|
||||
@ComponentDescriptor(name = "Blank Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, transformer = SimpleComponentTransformer.class)
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, transformer = SimpleComponentTransformer.class)
|
||||
public class BlankBoard extends AbstractBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -69,18 +69,18 @@ public class BlankBoard extends AbstractBoard {
|
||||
}
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
if (componentState != ComponentState.DRAGGING) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(boardColor);
|
||||
if (getType() == Type.SQUARE)
|
||||
g2d.fillRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
else
|
||||
g2d.fillOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
g2d.setComposite(oldComposite);
|
||||
// if (componentState != ComponentState.DRAGGING) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(boardColor);
|
||||
if (getType() == Type.SQUARE)
|
||||
g2d.fillRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
else
|
||||
g2d.fillOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
g2d.setComposite(oldComposite);
|
||||
// }
|
||||
// Do not track any changes that follow because the whole board has been
|
||||
// tracked so far.
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Breadboard", category = "Boards", author = "Branislav Stojkovic",
|
||||
description = "Prototyping solderless breadboard", instanceNamePrefix = "BB",
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true, transformer = ProtoBoardTransformer.class)
|
||||
public class Breadboard extends AbstractProtoBoard {
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
author = "Branislav Stojkovic",
|
||||
description = "David Haillant's eurorack format stripboard for prototyping and building simple modules",
|
||||
instanceNamePrefix = "BB", zOrder = IDIYComponent.BOARD,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true,
|
||||
transformer = ProtoBoardTransformer.class)
|
||||
public class EurorackStripboard extends AbstractProtoBoard {
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Eyelet Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with eyelets",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
transformer = AbstractBoardTransformer.class, enableCache = true)
|
||||
public class EyeletBoard extends AbstractBoard {
|
||||
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ import org.diylc.utils.Constants;
|
||||
@ComponentDescriptor(name = "Marshall Style Perf Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board",
|
||||
description = "Perforated board as found on some Marshall and Trainwreck amps",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
keywordTag = "Perf Board", transformer = AbstractBoardTransformer.class)
|
||||
public class MarshallPerfBoard extends AbstractBoard {
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Perf Board w/ Pads", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with solder pads",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TAG,
|
||||
keywordTag = "Perf Board", transformer = AbstractBoardTransformer.class, enableCache = true)
|
||||
public class PerfBoard extends AbstractBoard implements ILayeredComponent {
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "P-0+o (Proto) Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
description = "A prototyping board for modular synths, central bus with V+ GND and V-", instanceNamePrefix = "BB",
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true, transformer = ProtoBoardTransformer.class)
|
||||
public class ProtoBoard extends AbstractProtoBoard {
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
author = "Branislav Stojkovic",
|
||||
description = "Prototyping board similar to Radio Shack 276-168B or MPJA 33304",
|
||||
instanceNamePrefix = "BB", zOrder = IDIYComponent.BOARD,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true,
|
||||
transformer = ProtoBoardTransformer.class)
|
||||
public class ProtoBoard780 extends AbstractProtoBoard {
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.diylc.utils.Constants;
|
||||
@ComponentDescriptor(name = "Vero Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board",
|
||||
description = "Perforated FR4 board with copper strips connecting all holes in a row",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
transformer = VeroBoardTransformer.class, enableCache = true)
|
||||
public class VeroBoard extends AbstractVeroBoard implements ILayeredComponent {
|
||||
|
||||
|
||||
@@ -64,8 +64,15 @@ public class ChassisPanel extends AbstractShape {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(color);
|
||||
g2d.fillRoundRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(secondPoint.getX() - firstPoint.getX()), (int)(secondPoint.getY() - firstPoint.getY()), radius,
|
||||
radius);
|
||||
if (radius == 0) {
|
||||
g2d.fillRect((int) firstPoint.getX(), (int) firstPoint.getY(),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()));
|
||||
} else {
|
||||
g2d.fillRoundRect((int) firstPoint.getX(), (int) firstPoint.getY(),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()), radius, radius);
|
||||
}
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
// Do not track any changes that follow because the whole rect has been
|
||||
@@ -73,8 +80,15 @@ public class ChassisPanel extends AbstractShape {
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: borderColor);
|
||||
g2d.drawRoundRect((int)(firstPoint.getX()), (int)(firstPoint.getY()), (int)(secondPoint.getX() - firstPoint.getX()), (int)(secondPoint.getY() - firstPoint.getY()), radius,
|
||||
radius);
|
||||
if (radius == 0) {
|
||||
g2d.drawRect((int) (firstPoint.getX()), (int) (firstPoint.getY()),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()));
|
||||
} else {
|
||||
g2d.drawRoundRect((int) (firstPoint.getX()), (int) (firstPoint.getY()),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()), radius, radius);
|
||||
}
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Radius")
|
||||
|
||||
+16
-1
@@ -28,6 +28,7 @@ import com.bancika.gerberwriter.GerberFunctions;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.AbstractLeadedComponent;
|
||||
import org.diylc.components.LineEndingStyle;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.CreationMethod;
|
||||
@@ -47,7 +48,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
@ComponentDescriptor(name = "Copper Trace", author = "Branislav Stojkovic", category = "Connectivity",
|
||||
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "Trace",
|
||||
description = "Straight copper trace", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayeredComponent, IGerberComponentSimple {
|
||||
|
||||
@@ -58,6 +59,7 @@ public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayer
|
||||
|
||||
private Size thickness = THICKNESS;
|
||||
private PCBLayer layer = PCBLayer._1;
|
||||
private LineEndingStyle endingStyle = LineEndingStyle.Round;
|
||||
|
||||
public CopperTrace() {
|
||||
super();
|
||||
@@ -202,6 +204,19 @@ public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayer
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Ending Style")
|
||||
@Override
|
||||
public LineEndingStyle getEndingStyle() {
|
||||
if (endingStyle == null) {
|
||||
endingStyle = LineEndingStyle.Round;
|
||||
}
|
||||
return endingStyle;
|
||||
}
|
||||
|
||||
public void setEndingStyle(LineEndingStyle endingStyle) {
|
||||
this.endingStyle = endingStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GerberRenderMode> getGerberRenderModes() {
|
||||
return EnumSet.of(GerberRenderMode.Normal);
|
||||
|
||||
+18
-4
@@ -17,8 +17,7 @@
|
||||
*/
|
||||
package org.diylc.components.connectivity;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
@@ -29,6 +28,8 @@ import org.diylc.common.LineStyle;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.AbstractCurvedComponent;
|
||||
import org.diylc.components.AbstractLeadedComponent;
|
||||
import org.diylc.components.LineEndingStyle;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
@@ -46,7 +47,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Curved Trace", author = "Branislav Stojkovic", category = "Connectivity",
|
||||
instanceNamePrefix = "Trace", description = "Curved copper trace with two control points",
|
||||
zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class,
|
||||
enableCache = true)
|
||||
public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayeredComponent, IGerberComponentSimple {
|
||||
@@ -58,6 +59,7 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer
|
||||
|
||||
protected Size size = SIZE;
|
||||
private PCBLayer layer = PCBLayer._1;
|
||||
private LineEndingStyle endingStyle = LineEndingStyle.Round;
|
||||
|
||||
@Override
|
||||
protected Color getDefaultColor() {
|
||||
@@ -67,7 +69,7 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer
|
||||
@Override
|
||||
protected void drawCurve(Path2D curve, Graphics2D g2d, ComponentState componentState, IDrawingObserver drawingObserver) {
|
||||
float thickness = (float) size.convertToPixels();
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(thickness));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchStroke(thickness, getEndingStyle() == LineEndingStyle.Round ? BasicStroke.CAP_ROUND : BasicStroke.CAP_BUTT));
|
||||
Color curveColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: color;
|
||||
@@ -97,6 +99,18 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer
|
||||
public void setLayer(PCBLayer layer) {
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Ending Style")
|
||||
public LineEndingStyle getEndingStyle() {
|
||||
if (endingStyle == null) {
|
||||
endingStyle = LineEndingStyle.Round;
|
||||
}
|
||||
return endingStyle;
|
||||
}
|
||||
|
||||
public void setEndingStyle(LineEndingStyle endingStyle) {
|
||||
this.endingStyle = endingStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayerId() {
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Hookup Wire", author = "Branislav Stojkovic", category = "Connectivity",
|
||||
instanceNamePrefix = "W", description = "Flexible wire with two control points", zOrder = IDIYComponent.WIRING,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
transformer = SimpleComponentTransformer.class, enableCache = false)
|
||||
public class HookupWire extends AbstractCurvedComponent<AWG> implements IContinuity {
|
||||
|
||||
|
||||
+46
-24
@@ -28,21 +28,20 @@ import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation45;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
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 = "Multimeter Probe", category = "Connectivity",
|
||||
author = "Branislav Stojkovic", description = "Multimeter Probe", instanceNamePrefix = "Probe",
|
||||
@@ -59,6 +58,7 @@ public class MultimeterProbe extends AbstractTransparentComponent<Color> {
|
||||
public static Size HANDLE_DIAMETER = new Size(0.2d, SizeUnit.in);
|
||||
public static Size HANDLE_LENGTH = new Size(0.4d, SizeUnit.in);
|
||||
public static Color HANDLE_COLOR = Color.red;
|
||||
public static Size SENSITIVITY = new Size(0.05d, SizeUnit.in);
|
||||
|
||||
private Point2D.Double point = new Point2D.Double(0, 0);
|
||||
private Color handleColor = HANDLE_COLOR;
|
||||
@@ -77,27 +77,49 @@ public class MultimeterProbe extends AbstractTransparentComponent<Color> {
|
||||
Shape[] shapes = getBody();
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
|
||||
if (!outlineMode) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(PROBE_COLOR);
|
||||
g2d.fill(shapes[0]);
|
||||
g2d.setColor(handleColor);
|
||||
g2d.fill(shapes[1]);
|
||||
g2d.setComposite(oldComposite);
|
||||
// draw an invisible conductive circle around the point to increase sensitivity
|
||||
g2d.setColor(Constants.TRANSPARENT_COLOR);
|
||||
drawingObserver.startTrackingContinuityArea(true);
|
||||
double sensitivity = SENSITIVITY.convertToPixels();
|
||||
g2d.fillOval((int) (point.getX() - sensitivity), (int) (point.getY() - sensitivity),
|
||||
(int) (sensitivity * 2), (int) (sensitivity * 2));
|
||||
drawingObserver.stopTrackingContinuityArea();
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: handleColor.darker());
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : PROBE_COLOR);
|
||||
g2d.fill(shapes[0]);
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : handleColor);
|
||||
g2d.fill(shapes[1]);
|
||||
g2d.setComposite(oldComposite);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
|
||||
Constants.DEFAULT_THEME);
|
||||
|
||||
Color handleBorderColor;
|
||||
if (outlineMode) {
|
||||
handleBorderColor = theme.getOutlineColor();
|
||||
} else {
|
||||
handleBorderColor = componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: handleColor.darker();
|
||||
}
|
||||
g2d.setColor(handleBorderColor);
|
||||
g2d.draw(shapes[1]);
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: PROBE_COLOR.darker());
|
||||
|
||||
Color probeBorderColor;
|
||||
if (outlineMode) {
|
||||
probeBorderColor = theme.getOutlineColor();
|
||||
} else {
|
||||
probeBorderColor = componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: PROBE_COLOR.darker();
|
||||
}
|
||||
g2d.setColor(probeBorderColor);
|
||||
g2d.draw(shapes[0]);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "PCB Terminal Block", category = "Connectivity",
|
||||
author = "Branislav Stojkovic", description = "Horizontal PCB terminal block with 5mm pitch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "TB", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "TB",
|
||||
enableCache = true, transformer = PCBTerminalBlockTransformer.class)
|
||||
public class PCBTerminalBlock extends AbstractTransparentComponent<PCBTerminalBlockCount> implements IGerberComponentSimple {
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Solder Pad", category = "Connectivity", author = "Branislav Stojkovic",
|
||||
description = "Copper solder pad, round or square", instanceNamePrefix = "Pad",
|
||||
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class SolderPad extends AbstractTransparentComponent<Void>
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ import org.diylc.utils.Constants;
|
||||
@ComponentDescriptor(name = "Twisted Leads", author = "Branislav Stojkovic",
|
||||
category = "Connectivity", instanceNamePrefix = "W",
|
||||
description = "A pair of flexible leads twisted tighly together", zOrder = IDIYComponent.WIRING,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class TwistedWire extends AbstractCurvedComponent<Void> implements IContinuity {
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "9V Battery Snap", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "", zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "BTR",
|
||||
autoEdit = false, transformer = BatterySnapTransformer.class)
|
||||
transformer = BatterySnapTransformer.class)
|
||||
public class BatterySnap9V extends AbstractTransparentComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Cliff 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Cliff-style closed panel mount 1/4\" phono jack",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", autoEdit = false, transformer = CliffJackTransformer.class,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = CliffJackTransformer.class,
|
||||
enableCache = true)
|
||||
public class CliffJack1_4 extends AbstractMultiPartComponent<JackType> implements IGerberComponentSimple {
|
||||
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Cliff 1/8\" Jack", category = "Electro-Mechanical",
|
||||
author = "Branislav Stojkovic", description = "Cliff-style closed 1/8\" phono jack",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J",
|
||||
transformer = CliffJackTransformer.class, enableCache = true)
|
||||
public class CliffJack1_8 extends AbstractMultiPartComponent<JackType> implements IGerberComponentSimple {
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Closed 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Enclosed panel mount 1/4\" phono jack", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "J", autoEdit = false, transformer = ClosedJackTransformer.class, enableCache = true)
|
||||
instanceNamePrefix = "J", transformer = ClosedJackTransformer.class, enableCache = true)
|
||||
public class ClosedJack1_4 extends AbstractMultiPartComponent<JackType> implements IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
*
|
||||
* 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.*;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import com.bancika.gerberwriter.GerberFunctions;
|
||||
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
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.annotations.PositiveNonZeroMeasureValidator;
|
||||
import org.diylc.core.gerber.GerberLayer;
|
||||
import org.diylc.core.gerber.IGerberComponentCustom;
|
||||
import org.diylc.core.gerber.IGerberDrawingObserver;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Drill Hole", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Drill hole marker for PCB, chassis or faceplate", instanceNamePrefix = "Hole",
|
||||
zOrder = IDIYComponent.BOARD + 0.1, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class DrillHole extends AbstractTransparentComponent<Void>
|
||||
implements IGerberComponentCustom {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Size DIAMETER = new Size(3.0d, SizeUnit.mm);
|
||||
public static Color COLOR = Color.black;
|
||||
public static Size STROKE_WIDTH = new Size(1.0d, SizeUnit.px);
|
||||
public static Size CROSSHAIR_WIDTH = new Size(1.0d, SizeUnit.px);
|
||||
|
||||
private Size diameter = DIAMETER;
|
||||
private Color color = COLOR;
|
||||
private Point2D.Double point = new Point2D.Double(0, 0);
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver,
|
||||
IGerberDrawingObserver gerberDrawingObserver) {
|
||||
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
double holeDiameter = getDiameter().convertToPixels();
|
||||
double borderWidth = STROKE_WIDTH.convertToPixels(); // Use constant stroke width
|
||||
|
||||
// Export to Gerber drill layer
|
||||
if (gerberDrawingObserver != null) {
|
||||
GerberLayer drillLayer = GerberLayer.DrillNonPlated;
|
||||
gerberDrawingObserver.startGerberOutput(drillLayer, GerberFunctions.COMPONENT_DRILL, false);
|
||||
}
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (getAlpha() < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA));
|
||||
}
|
||||
|
||||
// Draw white circle (hole)
|
||||
g2d.setColor(Color.white);
|
||||
g2d.fill(new Ellipse2D.Double(point.getX() - holeDiameter / 2, point.getY() - holeDiameter / 2,
|
||||
holeDiameter, holeDiameter));
|
||||
|
||||
// Draw colored border
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: color);
|
||||
g2d.setStroke(new BasicStroke((float) borderWidth));
|
||||
g2d.draw(new Ellipse2D.Double(point.getX() - holeDiameter / 2, point.getY() - holeDiameter / 2,
|
||||
holeDiameter, holeDiameter));
|
||||
|
||||
// Draw crosshairs
|
||||
double crosshairLength = holeDiameter * 0.4; // 40% of diameter for crosshair length (increased from 30%)
|
||||
double crosshairWidth = CROSSHAIR_WIDTH.convertToPixels(); // Use constant crosshair width
|
||||
|
||||
g2d.setStroke(new BasicStroke((float) crosshairWidth));
|
||||
|
||||
// Horizontal line
|
||||
g2d.draw(new Line2D.Double(
|
||||
point.getX() - crosshairLength / 2, point.getY(),
|
||||
point.getX() + crosshairLength / 2, point.getY()));
|
||||
|
||||
// Vertical line
|
||||
g2d.draw(new Line2D.Double(
|
||||
point.getX(), point.getY() - crosshairLength / 2,
|
||||
point.getX(), point.getY() + crosshairLength / 2));
|
||||
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
if (gerberDrawingObserver != null) {
|
||||
gerberDrawingObserver.stopGerberOutput();
|
||||
}
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
this.draw(g2d, componentState, outlineMode, project, drawingObserver, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
int diameter = getClosestOdd(width * 3 / 4); // Increased from width/2 to 3/4 of width
|
||||
int borderWidth = Math.max(1, (int) STROKE_WIDTH.convertToPixels()); // Use constant stroke width
|
||||
int crosshairLength = diameter * 2 / 5; // 40% of diameter (increased from 1/3)
|
||||
int crosshairWidth = Math.max(1, (int) CROSSHAIR_WIDTH.convertToPixels()); // Use constant crosshair width
|
||||
|
||||
// Draw white circle
|
||||
g2d.setColor(Color.white);
|
||||
g2d.fillOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
|
||||
|
||||
// Draw colored border
|
||||
g2d.setColor(COLOR);
|
||||
g2d.setStroke(new BasicStroke(borderWidth));
|
||||
g2d.drawOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
|
||||
|
||||
// Draw crosshairs
|
||||
g2d.setStroke(new BasicStroke(crosshairWidth));
|
||||
|
||||
// Horizontal line
|
||||
g2d.drawLine(
|
||||
(width - crosshairLength) / 2, height / 2,
|
||||
(width + crosshairLength) / 2, height / 2);
|
||||
|
||||
// Vertical line
|
||||
g2d.drawLine(
|
||||
width / 2, (height - crosshairLength) / 2,
|
||||
width / 2, (height + crosshairLength) / 2);
|
||||
}
|
||||
|
||||
@EditableProperty(validatorClass = PositiveNonZeroMeasureValidator.class)
|
||||
public Size getDiameter() {
|
||||
return diameter;
|
||||
}
|
||||
|
||||
public void setDiameter(Size diameter) {
|
||||
this.diameter = diameter;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Color")
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return point;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
this.point.setLocation(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
double diameter = getDiameter().convertToPixels();
|
||||
return new Rectangle2D.Double(point.getX() - diameter, point.getY() - diameter,
|
||||
diameter * 2, diameter * 2);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Fuse Holder (Panel)", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Panel mounted fuse holder", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "FH", autoEdit = false, enableCache = true)
|
||||
instanceNamePrefix = "FH", enableCache = true)
|
||||
public class FuseHolderPanel extends AbstractMultiPartComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "IEC Socket", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Panel mounted IEC power socket", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "IEC", autoEdit = false, enableCache = true)
|
||||
instanceNamePrefix = "IEC", enableCache = true)
|
||||
public class IECSocket extends AbstractMultiPartComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+12
-5
@@ -59,7 +59,7 @@ import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Mini Toggle Switch", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Panel mounted mini toggle switch", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "SW", autoEdit = false, enableCache = true, transformer = MiniToggleSwitchTransformer.class)
|
||||
instanceNamePrefix = "SW", enableCache = true, transformer = MiniToggleSwitchTransformer.class)
|
||||
public class MiniToggleSwitch extends AbstractTransparentComponent<ToggleSwitchType> implements ISwitch, ILayeredComponent, IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -502,10 +502,17 @@ public class MiniToggleSwitch extends AbstractTransparentComponent<ToggleSwitchT
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
if (switchType.name().endsWith("_off") && position == 2)
|
||||
return "OFF";
|
||||
return "ON" + Integer.toString(position + 1);
|
||||
public String getPositionName(int position) {
|
||||
int pos = position;
|
||||
if (switchType.name().endsWith("_off")) {
|
||||
int midPosition = (getPositionCount() - 1) / 2;
|
||||
if (position == midPosition) {
|
||||
return "OFF";
|
||||
} else if (position > midPosition) {
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
return "ON" + Integer.toString(pos + 1);
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = ToggleSwitchPositionPropertyValueSource.class)
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Neutrik 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "1/4\" mono/stereo phono jack based on Neutrik NMJx series, PCB or Panel-mount",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", autoEdit = false, transformer = NeutrikJack1_4Transformer.class,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = NeutrikJack1_4Transformer.class,
|
||||
enableCache = true)
|
||||
public class NeutrikJack1_4 extends AbstractMultiPartComponent<JackType> {
|
||||
|
||||
|
||||
+2
-1
@@ -42,6 +42,7 @@ import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMultiPartComponent;
|
||||
import org.diylc.components.electromechanical.OpenJack1_4.OpenJackType;
|
||||
import org.diylc.components.transform.OpenJackTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -56,7 +57,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Open 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Switchcraft-style open panel mount 1/4\" phono jack, stereo and mono",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", enableCache = true)
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = OpenJackTransformer.class, enableCache = true)
|
||||
public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
|
||||
|
||||
private static final float FONT_SCALE = 0.8f;
|
||||
|
||||
+2
-1
@@ -42,6 +42,7 @@ import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMultiPartComponent;
|
||||
import org.diylc.components.electromechanical.OpenJack1_8.OpenJackType;
|
||||
import org.diylc.components.transform.OpenJackTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -56,7 +57,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Open 1/8\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Switchcraft-style open panel mount 1/8\" phono jack, stereo and mono",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", enableCache = true)
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = OpenJackTransformer.class, enableCache = true)
|
||||
public class OpenJack1_8 extends AbstractMultiPartComponent<OpenJackType> {
|
||||
|
||||
private static final float FONT_SCALE = 0.6f;
|
||||
|
||||
+2
-2
@@ -374,13 +374,13 @@ public class RotarySwitchOpen extends AbstractAngledComponent<RotarySwitchOpenTy
|
||||
// Draw body
|
||||
if (componentState != ComponentState.DRAGGING) {
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getColor());
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(body[0]);
|
||||
drawingObserver.stopTracking();
|
||||
}
|
||||
|
||||
g2d.setColor(finalBorderColor);
|
||||
drawingObserver.startTracking();
|
||||
g2d.draw(body[0]);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
// Draw pins
|
||||
for (int i = 1; i <= secondLevelStart; i++) {
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Slide Switch", category = "Electro-Mechanical",
|
||||
author = "Branislav Stojkovic", description = "Panel mounted slide switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
enableCache = true, transformer = MiniToggleSwitchTransformer.class)
|
||||
public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> implements ISwitch, IGerberComponentSimple {
|
||||
|
||||
|
||||
+133
-25
@@ -35,13 +35,10 @@ import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.AbstractLabeledComponent;
|
||||
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.components.guitar.LeverSwitchPositionPropertyValueSource;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.DynamicEditableProperty;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.gerber.IGerberComponentSimple;
|
||||
@@ -53,22 +50,21 @@ import org.diylc.utils.Constants;
|
||||
category = "Electro-Mechanical", instanceNamePrefix = "SW",
|
||||
description = "4-pin tactile momentary switch", zOrder = IDIYComponent.COMPONENT,
|
||||
keywordPolicy = KeywordPolicy.SHOW_VALUE, enableCache = true)
|
||||
public class TactileMicroSwitch extends AbstractLabeledComponent<String> implements IGerberComponentSimple {
|
||||
public class TactileMicroSwitch extends AbstractLabeledComponent<String> implements IGerberComponentSimple,
|
||||
ISwitch {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color BODY_COLOR = Color.lightGray;
|
||||
public static Color BORDER_COLOR = Color.gray.darker();
|
||||
public static Color PIN_COLOR = METAL_COLOR;
|
||||
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
public static Color INDENT_COLOR = Color.gray.darker();
|
||||
public static Color LABEL_COLOR = Color.white;
|
||||
public static Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
|
||||
public static int PIN_COUNT = 4;
|
||||
public static Size CASE_SIZE = new Size(6d, SizeUnit.mm);
|
||||
public static Size INDENT_SIZE = new Size(3d, SizeUnit.mm);
|
||||
private static Size PIN_SPACING = new Size(4.5d, SizeUnit.mm);
|
||||
private static Size ROW_SPACING = new Size(6.5d, SizeUnit.mm);
|
||||
private static final Color BODY_COLOR = Color.lightGray;
|
||||
private static final Color BORDER_COLOR = Color.gray.darker();
|
||||
private static final Color PIN_COLOR = METAL_COLOR;
|
||||
private static final Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
private static final Color INDENT_COLOR = Color.gray.darker();
|
||||
private static final Color LABEL_COLOR = Color.white;
|
||||
private static final Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
|
||||
private static final Size CASE_SIZE = new Size(6d, SizeUnit.mm);
|
||||
private static final Size PIN_SPACING = new Size(4.5d, SizeUnit.mm);
|
||||
private static final Size ROW_SPACING = new Size(6.5d, SizeUnit.mm);
|
||||
|
||||
private String value = "";
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
@@ -80,6 +76,12 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
private Color labelColor = LABEL_COLOR;
|
||||
transient private Area[] body;
|
||||
|
||||
private Size caseSize;
|
||||
private Size pinSpacing;
|
||||
private Size rowSpacing;
|
||||
private PinPairingMode pinPairingMode;
|
||||
private int selectedPosition;
|
||||
|
||||
public TactileMicroSwitch() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
@@ -146,10 +148,11 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
int PIN_COUNT = 4;
|
||||
controlPoints = new Point2D[PIN_COUNT];
|
||||
controlPoints[0] = firstPoint;
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
double pinSpacing = getPinSpacing().convertToPixels();
|
||||
double rowSpacing = getRowSpacing().convertToPixels();
|
||||
// Update control points.
|
||||
double dx1;
|
||||
double dy1;
|
||||
@@ -198,8 +201,8 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
double y = controlPoints[0].getY();
|
||||
double width;
|
||||
double height;
|
||||
double caseSize = CASE_SIZE.convertToPixels();
|
||||
double indentSize = INDENT_SIZE.convertToPixels();
|
||||
double caseSize = getCaseSize().convertToPixels();
|
||||
double actuatorSize = caseSize / 2;
|
||||
|
||||
width = height = caseSize;
|
||||
x = (controlPoints[0].getX() + controlPoints[3].getX() - width) / 2;
|
||||
@@ -207,7 +210,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
|
||||
body[0] = new Area(new Rectangle2D.Double(x, y, width, height));
|
||||
|
||||
width = height = indentSize;
|
||||
width = height = actuatorSize;
|
||||
x = (controlPoints[0].getX() + controlPoints[3].getX() - width) / 2;
|
||||
y = (controlPoints[0].getY() + controlPoints[3].getY() - height) / 2;
|
||||
|
||||
@@ -244,7 +247,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
|
||||
drawingObserver.stopTracking();
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(mainArea);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
@@ -391,6 +394,59 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
this.labelColor = labelColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Case Size")
|
||||
public Size getCaseSize() {
|
||||
if (caseSize == null) {
|
||||
caseSize = CASE_SIZE;
|
||||
}
|
||||
return caseSize;
|
||||
}
|
||||
|
||||
public void setCaseSize(Size caseSize) {
|
||||
this.caseSize = caseSize;
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Spacing")
|
||||
public Size getPinSpacing() {
|
||||
if (pinSpacing == null) {
|
||||
pinSpacing = PIN_SPACING;
|
||||
}
|
||||
return pinSpacing;
|
||||
}
|
||||
|
||||
public void setPinSpacing(Size pinSpacing) {
|
||||
this.pinSpacing = pinSpacing;
|
||||
this.body = null;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Row Spacing")
|
||||
public Size getRowSpacing() {
|
||||
if (rowSpacing == null) {
|
||||
rowSpacing = ROW_SPACING;
|
||||
}
|
||||
return rowSpacing;
|
||||
}
|
||||
|
||||
public void setRowSpacing(Size rowSpacing) {
|
||||
this.rowSpacing = rowSpacing;
|
||||
this.body = null;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Pairing Mode")
|
||||
public PinPairingMode getPinPairingMode() {
|
||||
if (pinPairingMode == null) {
|
||||
pinPairingMode = PinPairingMode.Opposite;
|
||||
}
|
||||
return pinPairingMode;
|
||||
}
|
||||
|
||||
public void setPinPairingMode(PinPairingMode pinPairingMode) {
|
||||
this.pinPairingMode = pinPairingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
@@ -418,4 +474,56 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin,
|
||||
maxY - minY + 2 * margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return position == 0 ? "Released" : "Pressed";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
// Only connect pins when switch is pressed (position = 1)
|
||||
if (position != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PinPairingMode mode = getPinPairingMode();
|
||||
|
||||
switch (mode) {
|
||||
case Opposite:
|
||||
// Pins on opposite sides (same row): 0-2 and 1-3
|
||||
return (index1 == 0 && index2 == 2) || (index1 == 2 && index2 == 0) ||
|
||||
(index1 == 1 && index2 == 3) || (index1 == 3 && index2 == 1);
|
||||
case Diagonal:
|
||||
// Diagonally opposite pins: 0-3 and 1-2
|
||||
return (index1 == 0 && index2 == 3) || (index1 == 3 && index2 == 0) ||
|
||||
(index1 == 1 && index2 == 2) || (index1 == 2 && index2 == 1);
|
||||
case Adjacent:
|
||||
// Pins next to each other (same column): 0-1 and 2-3
|
||||
return (index1 == 0 && index2 == 1) || (index1 == 1 && index2 == 0) ||
|
||||
(index1 == 2 && index2 == 3) || (index1 == 3 && index2 == 2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = TactileMicroSwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
public Integer getSelectedPosition() {
|
||||
return selectedPosition;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(Integer selectedPosition) {
|
||||
this.selectedPosition = selectedPosition;
|
||||
}
|
||||
|
||||
public enum PinPairingMode {
|
||||
Opposite, Diagonal, Adjacent;
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.diylc.components.electromechanical;
|
||||
|
||||
import org.diylc.common.PropertyWrapper;
|
||||
import org.diylc.components.guitar.LeverSwitch;
|
||||
import org.diylc.core.AbstractDynamicPropertyValueSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class TactileMicroSwitchPositionPropertyValueSource extends AbstractDynamicPropertyValueSource {
|
||||
|
||||
@Override
|
||||
public List<Object> getAvailableValues() {
|
||||
return List.of("Released", "Pressed");
|
||||
}
|
||||
}
|
||||
@@ -59,8 +59,8 @@ import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Freeway 3X3-03 Toggle", category = "Guitar",
|
||||
author = "Branislav Stojkovic", description = "Freeway 3X3-03 Toggle Switch",
|
||||
@ComponentDescriptor(name = "Freeway 3X3-03 Switch", category = "Guitar",
|
||||
author = "Branislav Stojkovic", description = "Freeway 3X3-03 (Standard) Toggle Switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
transformer = Freeway3x3_03Transformer.class, enableCache = true)
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
*
|
||||
* 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.guitar;
|
||||
|
||||
import com.kitfox.svg.SVGDiagram;
|
||||
import com.kitfox.svg.SVGElement;
|
||||
import com.kitfox.svg.SVGUniverse;
|
||||
import com.kitfox.svg.ShapeElement;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.*;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.Freeway3x3_03Transformer;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.DynamicEditableProperty;
|
||||
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;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.diylc.core.measures.SizeUnit.px;
|
||||
|
||||
@ComponentDescriptor(name = "Freeway 3X3-05 (Ultra) Switch", category = "Guitar",
|
||||
author = "Martin Morrell", description = "Freeway 3X3-05 (Ultra) Toggle Switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
transformer = Freeway3x3_03Transformer.class, enableCache = false)
|
||||
public class Freeway3x3_05 extends AbstractTransparentComponent<Void> implements ISwitch {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(Freeway3x3_05.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static Color BASE_COLOR = FR4_COLOR;// Color.DARK_GRAY;
|
||||
private static Color PAD_COLOR = COPPER_COLOR;
|
||||
private static Color LABEL_COLOR = Color.WHITE;
|
||||
private static Color CASE_COLOR = METAL_COLOR;
|
||||
private static Color MARKER_COLOR = Color.lightGray;
|
||||
|
||||
private static final double DIAMETER_SWITCH = 34.0;
|
||||
private static final double PAD_RADIUS = 15.3;
|
||||
private static final double PAD_SIZE = 1.5;
|
||||
private static final int NUM_CONTACTS = 28;
|
||||
private static final double CONTACT_SPACING_RAD = Math.PI * (360.0 / NUM_CONTACTS) / 180.0;
|
||||
private static final double CONTACT_SPACING_INITIAL = (CONTACT_SPACING_RAD * 14) + CONTACT_SPACING_RAD / 2;
|
||||
private static final double GROUND_DIAMETER = 20;
|
||||
private static final double CENTRE_LEN = 17;
|
||||
private static final double CENTRE_WID = 10;
|
||||
private static final double MARKER_OFFSET = 3.1;
|
||||
|
||||
private static final String[] PAD_NAMES = new String[] {
|
||||
"1B", "2B", "3B", "6B", "5B", "4B", "CB", "TB", "6B", "5B", "4B", "1B", "2B", "3B",
|
||||
"6A", "5A", "4A", "1A", "2A", "3A", "TA", "CA", "1A", "2A", "3A", "6A", "5A", "4A"
|
||||
};
|
||||
|
||||
private static final AffineTransform SCALE_TX = AffineTransform.getScaleInstance(0.615d, 0.615d);
|
||||
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
||||
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
|
||||
private Integer selectedPosition;
|
||||
private Boolean showMarkers;
|
||||
|
||||
private transient SVGDiagram svgDiagram;
|
||||
private transient List<Shape> pads;
|
||||
private transient List<Shape> labels;
|
||||
private transient Shape base;
|
||||
private transient Shape caseShape;
|
||||
private transient double[] xOffsetsPx;
|
||||
private transient double[] yOffsetsPx;
|
||||
|
||||
public Freeway3x3_05() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return PAD_NAMES[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
|
||||
if (getOrientation() != Orientation.DEFAULT) {
|
||||
double theta = orientation.toRadians();
|
||||
g2d.rotate(theta, x, y);
|
||||
}
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
|
||||
double offsetX = getXOffsetsPx()[0];
|
||||
double offsetY = getYOffsetsPx()[0];
|
||||
//double origin = DIAMETER_SWITCH / 2;
|
||||
|
||||
int diameter = (int)(new Size(DIAMETER_SWITCH, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
double originX = x - offsetX + diameter / 2.0;
|
||||
double originY = y - offsetY + diameter / 2.0;
|
||||
|
||||
//Main circular shape
|
||||
Color finalBorderColor;
|
||||
if (outlineMode) {
|
||||
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
|
||||
Constants.DEFAULT_THEME);
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: BASE_COLOR.darker();
|
||||
}
|
||||
Shape base = new Ellipse2D.Double( originX, originY, diameter, diameter);
|
||||
drawingObserver.startTracking();
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(base);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setStroke(new BasicStroke(1f));
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.draw(base);
|
||||
|
||||
//Ground circular shape
|
||||
int gndDiameter = (int)(new Size(GROUND_DIAMETER, SizeUnit.mm)).convertToPixels();
|
||||
int gndShift = (int)(new Size( (DIAMETER_SWITCH - GROUND_DIAMETER) / 2, SizeUnit.mm).convertToPixels());
|
||||
Shape gnd = new Ellipse2D.Double( originX + gndShift, originY + gndShift, gndDiameter, gndDiameter);
|
||||
g2d.setPaint(PAD_COLOR);
|
||||
g2d.fill(gnd);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(gnd);
|
||||
|
||||
//Centre rectangle casing block
|
||||
int rectLen = (int)(new Size(CENTRE_LEN, SizeUnit.mm)).convertToPixels();
|
||||
int rectWid = (int)(new Size(CENTRE_WID, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
int rectShiftX = (int)(new Size( (DIAMETER_SWITCH - CENTRE_WID) / 2, SizeUnit.mm).convertToPixels());
|
||||
int rectShiftY = (int)(new Size( (DIAMETER_SWITCH - CENTRE_LEN) / 2, SizeUnit.mm).convertToPixels());
|
||||
|
||||
int rectangleArc = (int)(new Size(1.0, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
Shape casing = new RoundRectangle2D.Double(originX + rectShiftX, originY + rectShiftY, rectWid, rectLen, rectangleArc, rectangleArc);
|
||||
|
||||
g2d.setPaint(CASE_COLOR);
|
||||
g2d.fill(casing);
|
||||
g2d.setColor(CASE_COLOR.darker());
|
||||
g2d.draw(casing);
|
||||
|
||||
//Pads
|
||||
double circleShift = (int)(new Size( DIAMETER_SWITCH / 2, SizeUnit.mm).convertToPixels());
|
||||
double circleCentreX = originX + circleShift;
|
||||
double circleCentreY = originY + circleShift;
|
||||
double radChange = CONTACT_SPACING_RAD * 0.45;
|
||||
double extendedRadius = (int)new Size(PAD_RADIUS + PAD_SIZE, SizeUnit.mm).convertToPixels();
|
||||
double reducedRadius = (int)new Size(PAD_RADIUS - PAD_SIZE, SizeUnit.mm).convertToPixels();
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double rad = ((i + 0.5) * CONTACT_SPACING_RAD);
|
||||
|
||||
double rotExX = Math.cos(rad) * (extendedRadius * 1.01) + circleCentreX;
|
||||
double rotExY = Math.sin(rad) * (extendedRadius * 1.01) + circleCentreY;
|
||||
double rotExPosX = Math.cos(rad + radChange) * extendedRadius + circleCentreX;
|
||||
double rotExPosY = Math.sin(rad + radChange) * extendedRadius + circleCentreY;
|
||||
double rotExNegX = Math.cos(rad - radChange) * extendedRadius + circleCentreX;
|
||||
double rotExNegY = Math.sin(rad - radChange) * extendedRadius + circleCentreY;
|
||||
double rotReX = Math.cos(rad) * (reducedRadius * 1.01) + circleCentreX;
|
||||
double rotReY = Math.sin(rad) * (reducedRadius * 1.01) + circleCentreY;
|
||||
double rotRePosX = Math.cos(rad + radChange) * reducedRadius + circleCentreX;
|
||||
double rotRePosY = Math.sin(rad + radChange) * reducedRadius + circleCentreY;
|
||||
double rotReNegX = Math.cos(rad - radChange) * reducedRadius + circleCentreX;
|
||||
double rotReNegY = Math.sin(rad - radChange) * reducedRadius + circleCentreY;
|
||||
|
||||
Path2D pad = new Path2D.Double();
|
||||
pad.moveTo(rotExNegX, rotExNegY);
|
||||
pad.quadTo(rotExX, rotExY, rotExPosX, rotExPosY);
|
||||
pad.lineTo(rotRePosX, rotRePosY);
|
||||
pad.quadTo(rotReX, rotReY,rotReNegX, rotReNegY);
|
||||
pad.lineTo(rotExNegX, rotExNegY);
|
||||
pad.closePath();
|
||||
|
||||
g2d.setPaint(PAD_COLOR);
|
||||
g2d.fill(pad);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(pad);
|
||||
}
|
||||
|
||||
//Labels
|
||||
double offset = (int) new Size(MARKER_OFFSET, SizeUnit.mm).convertToPixels();
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
|
||||
double px = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS - MARKER_OFFSET);
|
||||
double py = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS - MARKER_OFFSET);
|
||||
|
||||
g2d.setPaint(LABEL_COLOR);
|
||||
StringUtils.drawCenteredText(g2d, PAD_NAMES[i],
|
||||
(new Size(px + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originX,
|
||||
(new Size(py + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
//Add other text
|
||||
int makeLen = (int)(new Size(12.0, SizeUnit.mm)).convertToPixels();
|
||||
int modelLen = (int)(new Size(8.0, SizeUnit.mm)).convertToPixels();
|
||||
double mWidth = 1.8;
|
||||
int modelWid = (int)(new Size(mWidth, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
int makeShiftX = (int)(new Size( (DIAMETER_SWITCH / 2) - 7.25, SizeUnit.mm).convertToPixels());
|
||||
int makeShiftY = (int)(new Size( (DIAMETER_SWITCH / 2) - 6.0, SizeUnit.mm).convertToPixels());
|
||||
int modelShiftX = (int)(new Size( (DIAMETER_SWITCH / 2) + 5.25, SizeUnit.mm).convertToPixels());
|
||||
int modelShiftY = (int)(new Size( (DIAMETER_SWITCH / 2) - 4.0, SizeUnit.mm).convertToPixels());
|
||||
|
||||
int modelArc = (int)(new Size(mWidth, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
Shape make = new RoundRectangle2D.Double(originX + makeShiftX, originY + makeShiftY, modelWid, makeLen, modelArc, modelArc);
|
||||
Shape model = new RoundRectangle2D.Double(originX + modelShiftX, originY + modelShiftY, modelWid, modelLen, modelArc, modelArc);
|
||||
|
||||
g2d.setPaint(BASE_COLOR);
|
||||
g2d.fill(make);
|
||||
g2d.fill(model);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(make);
|
||||
g2d.draw(model);
|
||||
|
||||
/*String makeTxt = "FREE-WAY·ULTRA";
|
||||
String modelTxt = "3X3-05";
|
||||
g2d.setPaint(LABEL_COLOR);
|
||||
StringUtils.drawCenteredText(g2d, makeTxt,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originX + makeShiftX,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originY + makeShiftY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, modelTxt,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originX + modelShiftX,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originY + modelShiftY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);*/
|
||||
|
||||
if (getShowMarkers()) {
|
||||
g2d.setColor(MARKER_COLOR);
|
||||
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
Point2D labelPoint = new Point2D.Double(offset, 0);
|
||||
|
||||
double px = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS + MARKER_OFFSET);
|
||||
double py = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS + MARKER_OFFSET);
|
||||
|
||||
String marker = PAD_NAMES[i];
|
||||
if(marker.equals("CA")) {
|
||||
marker = "A";
|
||||
} else if (marker.equals("CB")) {
|
||||
marker = "B";
|
||||
} else if (marker.startsWith("T")) {
|
||||
continue;
|
||||
} else {
|
||||
marker = marker.substring(0, 1);
|
||||
}
|
||||
|
||||
StringUtils.drawCenteredText(g2d, marker,
|
||||
(new Size(px + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originX,
|
||||
(new Size(py + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
public double[] getXOffsetsPx() {
|
||||
if (xOffsetsPx == null) {
|
||||
xOffsetsPx = new double[NUM_CONTACTS];
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double x = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * PAD_RADIUS;
|
||||
xOffsetsPx[i] = new Size(x + DIAMETER_SWITCH, SizeUnit.mm).convertToPixels();
|
||||
}
|
||||
}
|
||||
return xOffsetsPx;
|
||||
}
|
||||
|
||||
public double[] getYOffsetsPx() {
|
||||
if (yOffsetsPx == null) {
|
||||
yOffsetsPx = new double[NUM_CONTACTS];
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double y = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * PAD_RADIUS;
|
||||
yOffsetsPx[i] = new Size(y + DIAMETER_SWITCH , SizeUnit.mm).convertToPixels();
|
||||
}
|
||||
}
|
||||
return yOffsetsPx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
Shape base = new Ellipse2D.Double( 0, 0, width, height);
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(base);
|
||||
|
||||
double contactOffset = 1;
|
||||
Shape contacts = new Ellipse2D.Double( contactOffset, contactOffset, width - contactOffset * 2, height - contactOffset * 2);
|
||||
g2d.setColor(PAD_COLOR);
|
||||
g2d.fill(contacts);
|
||||
|
||||
double centreOffset = 4;
|
||||
Shape centre = new Ellipse2D.Double( centreOffset, centreOffset, width - centreOffset * 2, height - centreOffset * 2);
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(centre);
|
||||
|
||||
double gndOffset = 9;
|
||||
Shape gnd = new Ellipse2D.Double( gndOffset, gndOffset, width - gndOffset * 2, height - gndOffset * 2);
|
||||
g2d.setColor(PAD_COLOR);
|
||||
g2d.fill(gnd);
|
||||
|
||||
double arc = 2.0;
|
||||
Shape casing = new RoundRectangle2D.Double(12, 10, 8, 12, arc, arc);
|
||||
g2d.setColor(CASE_COLOR);
|
||||
g2d.fill(casing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return NUM_CONTACTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return controlPoints[index];
|
||||
}
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
controlPoints = new Point2D[NUM_CONTACTS];
|
||||
controlPoints[0] = firstPoint;
|
||||
double[] xOffsetsPx = getXOffsetsPx();
|
||||
double[] yOffsetsPx = getYOffsetsPx();
|
||||
double theta = orientation.toRadians();
|
||||
AffineTransform tx =
|
||||
AffineTransform.getRotateInstance(theta, firstPoint.getX(), firstPoint.getY());
|
||||
for (int i = 1; i < controlPoints.length; i++) {
|
||||
controlPoints[i] = new Point2D.Double(firstPoint.getX() + (xOffsetsPx[i] - xOffsetsPx[0]),
|
||||
firstPoint.getY() + (yOffsetsPx[i] - yOffsetsPx[0]));
|
||||
tx.transform(controlPoints[i], controlPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.ALWAYS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
controlPoints[index].setLocation(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Orientation getOrientation() {
|
||||
if (orientation == null)
|
||||
orientation = Orientation.DEFAULT;
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
this.orientation = orientation;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return Integer.toString(position + 1);
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = Freeway3x4_03SwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
public Integer getSelectedPosition() {
|
||||
return selectedPosition;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(Integer selectedPosition) {
|
||||
this.selectedPosition = selectedPosition;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
public Boolean getShowMarkers() {
|
||||
if (showMarkers == null) {
|
||||
showMarkers = false;
|
||||
}
|
||||
return showMarkers;
|
||||
}
|
||||
|
||||
public void setShowMarkers(Boolean showMarkers) {
|
||||
this.showMarkers = showMarkers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
if (position == 0 &&
|
||||
((index1 == 0 && index2 == 6) || (index1 == 6 && index2 == 11) || (index1 == 0 && index2 == 11) ||
|
||||
(index1 == 17 && index2 == 21) || (index1 == 21 && index2 == 22) || (index1 == 17 && index2 == 22))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 1 &&
|
||||
((index1 == 1 && index2 == 6) || (index1 == 6 && index2 == 12) || (index1 == 1 && index2 == 12) ||
|
||||
(index1 == 18 && index2 == 21) || (index1 == 21 && index2 == 23) || (index1 == 18 && index2 == 23))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 2 &&
|
||||
((index1 == 2 && index2 == 6) || (index1 == 6 && index2 == 13) || (index1 == 2 && index2 == 13) ||
|
||||
(index1 == 19 && index2 == 21) || (index1 == 21 && index2 == 24) || (index1 == 19 && index2 == 24))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 3 &&
|
||||
((index1 == 5 && index2 == 6) || (index1 == 6 && index2 == 10) || (index1 == 5 && index2 == 10) ||
|
||||
(index1 == 16 && index2 == 21) || (index1 == 21 && index2 == 27) || (index1 == 16 && index2 == 27))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 4 &&
|
||||
((index1 == 4 && index2 == 6) || (index1 == 6 && index2 == 9) || (index1 == 4 && index2 == 9) ||
|
||||
(index1 == 15 && index2 == 21) || (index1 == 21 && index2 == 26) || (index1 == 15 && index2 == 26))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 5 &&
|
||||
((index1 == 3 && index2 == 6) || (index1 == 6 && index2 == 8) || (index1 == 3 && index2 == 8) ||
|
||||
(index1 == 14 && index2 == 21) || (index1 == 21 && index2 == 25) || (index1 == 14 && index2 == 25))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Humbucker Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Double-coil humbucker guitar pickup (PAF, Mini Humbuckers, Filtertrons)",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class HumbuckerPickup extends AbstractGuitarPickup {
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Jazz Bass Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Single coil pickup for Jazz Bass and similar guitars",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
@@ -63,15 +63,18 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
private static Size LENGTH = new Size(1.3d, SizeUnit.in);
|
||||
private static Size BASE_LENGTH = new Size(18d, SizeUnit.mm);
|
||||
private static Size WAFER_THICKNESS = new Size(0.05d, SizeUnit.in);
|
||||
private static Size TERMINAL_SPACING = new Size(0.2d, SizeUnit.in);
|
||||
private static Size TERMINAL_SPACING_CONNECTED = new Size(0.2d, SizeUnit.in);
|
||||
private static Size TERMINAL_SPACING_DISCONNECTED = new Size(0.1d, SizeUnit.in);
|
||||
|
||||
private String value = "";
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0), new Point2D.Double(0, 0), new Point2D.Double(0, 0), new Point2D.Double(0, 0)};
|
||||
transient Shape[] body;
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
private MiddleTerminalConfiguration middleTerminalConfiguration;
|
||||
|
||||
public LPSwitch() {
|
||||
super();
|
||||
middleTerminalConfiguration = MiddleTerminalConfiguration.Connected;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@@ -127,7 +130,7 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.draw(body[1]);
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(3));
|
||||
g2d.setColor(METAL_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.draw(body[2]);
|
||||
@@ -178,24 +181,44 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
}
|
||||
}
|
||||
|
||||
int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
|
||||
|
||||
GeneralPath terminalPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
terminalPath.moveTo(x - waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected) {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_CONNECTED.convertToPixels();
|
||||
terminalPath.moveTo(x - waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
|
||||
terminalPath.moveTo(x - waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x - terminalSpacing, y + length);
|
||||
terminalPath.moveTo(x + waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing, y + length);
|
||||
terminalPath.moveTo(x - waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x - terminalSpacing, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing, y + length);
|
||||
} else {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_DISCONNECTED.convertToPixels();
|
||||
terminalPath.moveTo(x - waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x- terminalSpacing, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing, y + length);
|
||||
|
||||
terminalPath.moveTo(x - waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x - terminalSpacing * 2, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing * 2, y + length);
|
||||
}
|
||||
body[2] = terminalPath;
|
||||
|
||||
// Rotate if needed
|
||||
@@ -219,14 +242,37 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
|
||||
@SuppressWarnings("incomplete-switch")
|
||||
private void updateControlPoints() {
|
||||
|
||||
Point2D[] newControlPoints = null;
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected && controlPoints.length == 5) {
|
||||
newControlPoints = new Point2D[4];
|
||||
} else if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Disconnected && controlPoints.length == 4) {
|
||||
newControlPoints = new Point2D[5];
|
||||
newControlPoints[4] = new Point2D.Double();
|
||||
}
|
||||
if (newControlPoints != null) {
|
||||
for (int i = 0; i < newControlPoints.length && i < controlPoints.length; i++) {
|
||||
newControlPoints[i] = controlPoints[i];
|
||||
}
|
||||
controlPoints = newControlPoints;
|
||||
}
|
||||
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
|
||||
int length = (int) LENGTH.convertToPixels();
|
||||
|
||||
controlPoints[1].setLocation(x - terminalSpacing, y + length);
|
||||
controlPoints[2].setLocation(x, y + length);
|
||||
controlPoints[3].setLocation(x + terminalSpacing, y + length);
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected) {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_CONNECTED.convertToPixels();
|
||||
controlPoints[1].setLocation(x - terminalSpacing, y + length);
|
||||
controlPoints[2].setLocation(x, y + length);
|
||||
controlPoints[3].setLocation(x + terminalSpacing, y + length);
|
||||
} else {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_DISCONNECTED.convertToPixels();
|
||||
controlPoints[1].setLocation(x - terminalSpacing * 2, y + length);
|
||||
controlPoints[2].setLocation(x - terminalSpacing, y + length);
|
||||
controlPoints[3].setLocation(x + terminalSpacing, y + length);
|
||||
controlPoints[4].setLocation(x + terminalSpacing * 2, y + length);
|
||||
}
|
||||
|
||||
// Rotate if needed
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
@@ -331,8 +377,24 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
// Invalidate the body
|
||||
body = null;
|
||||
}
|
||||
|
||||
// @Override
|
||||
|
||||
@EditableProperty(name = "Middle Terminals")
|
||||
public MiddleTerminalConfiguration getMiddleTerminalConfiguration() {
|
||||
if (middleTerminalConfiguration == null) {
|
||||
middleTerminalConfiguration = MiddleTerminalConfiguration.Connected;
|
||||
}
|
||||
return middleTerminalConfiguration;
|
||||
}
|
||||
|
||||
public void setMiddleTerminalConfiguration(
|
||||
MiddleTerminalConfiguration middleTerminalConfiguration) {
|
||||
this.middleTerminalConfiguration = middleTerminalConfiguration;
|
||||
updateControlPoints();
|
||||
// Invalidate the body
|
||||
body = null;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String getControlPointNodeName(int index) {
|
||||
// // we don't want the switch to produce any nodes, it just makes connections
|
||||
// return null;
|
||||
@@ -347,27 +409,34 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return "Treble";
|
||||
case 1:
|
||||
return "Middle";
|
||||
case 2:
|
||||
return "Rhythm";
|
||||
}
|
||||
return null;
|
||||
return switch (position) {
|
||||
case 0 -> "Treble";
|
||||
case 1 -> "Middle";
|
||||
case 2 -> "Rhythm";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return index1 == 1 && index2 == 2;
|
||||
case 1:
|
||||
return index1 > 0 && index2 > 0 && index1 < index2;
|
||||
case 2:
|
||||
return index1 == 2 && index2 == 3;
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected) {
|
||||
return switch (position) {
|
||||
case 0 -> index1 == 1 && index2 == 2;
|
||||
case 1 -> index1 > 0 && index2 > 0 && index1 < index2;
|
||||
case 2 -> index1 == 2 && index2 == 3;
|
||||
default -> false;
|
||||
};
|
||||
} else {
|
||||
return switch (position) {
|
||||
case 0 -> index1 == 1 && index2 == 2;
|
||||
case 1 -> (index1 == 1 && index2 == 2) || (index1 == 3 && index2 == 4);
|
||||
case 2 -> index1 == 3 && index2 == 4;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public enum MiddleTerminalConfiguration {
|
||||
Connected, Disconnected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,13 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
g2d.draw(body[2]);
|
||||
|
||||
if (getShowMarkers()) {
|
||||
String[] markers =
|
||||
SwitchUtils.getSwitchingMarkers(this, getControlPointCount(), false);
|
||||
String[] markers;
|
||||
if(type == LeverSwitchType.DP3T_5pos_2502N) {
|
||||
markers = new String[]{"1", "2", "3", "4", "5", "6", "7", "8"};
|
||||
} else {
|
||||
markers = SwitchUtils.getSwitchingMarkers(this, getControlPointCount(), false);
|
||||
}
|
||||
|
||||
double theta = getAngle().getValueRad();
|
||||
g2d.setColor(MARKER_COLOR);
|
||||
|
||||
@@ -203,11 +208,13 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
|| type == LeverSwitchType.DP3T_5pos) {
|
||||
x += terminalLength;
|
||||
yOffset = 7;
|
||||
} else if (type == LeverSwitchType.DP3T_5pos_Import) {
|
||||
} else if (type == LeverSwitchType.DP3T_5pos_Import || type == LeverSwitchType.DP3T_5pos_2502N) {
|
||||
yOffset = 10;
|
||||
} else if (type == LeverSwitchType._6_WAY_OG) {
|
||||
x += terminalLength;
|
||||
yOffset = 8;
|
||||
} else if (type == LeverSwitchType._4P3T) {
|
||||
yOffset = 8;
|
||||
} else {
|
||||
yOffset = 12;
|
||||
}
|
||||
@@ -226,11 +233,21 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
- (waferLength - terminalSpacing * yOffset) / 2, waferThickness,
|
||||
waferLength));
|
||||
|
||||
if (type == LeverSwitchType._4P5T) {
|
||||
if (type == LeverSwitchType._4P5T || type == LeverSwitchType._6P5T) {
|
||||
waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2 + waferSpacing, y
|
||||
- (waferLength - terminalSpacing * 12) / 2, waferThickness,
|
||||
waferLength)));
|
||||
}
|
||||
if (type == LeverSwitchType._6P5T) {
|
||||
waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2 + waferSpacing * 2, y
|
||||
- (waferLength - terminalSpacing * 12) / 2, waferThickness,
|
||||
waferLength)));
|
||||
}
|
||||
if (type == LeverSwitchType._4P3T) {
|
||||
waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2 - waferThickness / 2 + waferSpacing, y
|
||||
- (waferLength - terminalSpacing * 8) / 2, waferThickness,
|
||||
waferLength)));
|
||||
}
|
||||
|
||||
double theta = getAngle().getValueRad();
|
||||
|
||||
@@ -294,6 +311,7 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
}
|
||||
break;
|
||||
case DP3T_5pos_Import:
|
||||
case DP3T_5pos_2502N:
|
||||
controlPoints = new Point2D[8];
|
||||
terminalSpacing *= 1.5;
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
@@ -303,7 +321,8 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
case DP4T:
|
||||
controlPoints = new Point2D[10];
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
controlPoints[i] = new Point2D.Double(x + (i % 2 == 1 ? terminalLength : 0), y + i * terminalSpacing);
|
||||
double yPos = i % 2 == 1 ? y + (i - 2) * terminalSpacing : y + i * terminalSpacing;
|
||||
controlPoints[i] = new Point2D.Double(x + (i % 2 == 1 ? terminalLength : 0), yPos);
|
||||
}
|
||||
break;
|
||||
case DP5T:
|
||||
@@ -319,6 +338,21 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
controlPoints[i + 12] = new Point2D.Double(x + waferSpacing, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
}
|
||||
break;
|
||||
case _6P5T:
|
||||
controlPoints = new Point2D[36];
|
||||
for (int i = 0; i < controlPoints.length / 3; i++) {
|
||||
controlPoints[i] = new Point2D.Double(x, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
controlPoints[i + 12] = new Point2D.Double(x + waferSpacing, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
controlPoints[i + 24] = new Point2D.Double(x + waferSpacing * 2, y + i * terminalSpacing + (i >= 6 ? terminalSpacing : 0));
|
||||
}
|
||||
break;
|
||||
case _4P3T:
|
||||
controlPoints = new Point2D[16];
|
||||
for (int i = 0; i < controlPoints.length / 2; i++) {
|
||||
controlPoints[i] = new Point2D.Double(x, y + i * terminalSpacing + (i >= 4 ? terminalSpacing : 0));
|
||||
controlPoints[i + 8] = new Point2D.Double(x + waferSpacing, y + i * terminalSpacing + (i >= 4 ? terminalSpacing : 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Rotate if needed
|
||||
@@ -447,7 +481,16 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
|| (index2 == 11 && index2 - index1 == 5 - position);
|
||||
case _4P5T:
|
||||
return ((index1 == 0 || index1 == 12) && index2 - index1 == position + 1)
|
||||
|| ((index2 == 11 || index2 == 23) && index2 - index1 == 5 - position);
|
||||
|| ((index2 == 11 || index2 == 23) && index2 - index1 == 5 - position);
|
||||
case _6P5T:
|
||||
return ((index1 == 0 || index1 == 12 || index1 == 24) && index2 - index1 == position + 1)
|
||||
|| ((index2 == 11 || index2 == 23 || index2 == 35) && index2 - index1 == 5 - position);
|
||||
case _4P3T:
|
||||
return ((index1 == 0 || index1 == 8) && index2 - index1 == position + 1)
|
||||
|| ((index2 == 7 || index2 == 15) && index2 - index1 == 3 - position);
|
||||
case DP3T_5pos_2502N:
|
||||
positionConnections = DP3T_5pos_2502N_CONNECTIONS.get(position);
|
||||
break;
|
||||
}
|
||||
|
||||
if (positionConnections != null) {
|
||||
@@ -526,6 +569,32 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
) // position 6
|
||||
);
|
||||
|
||||
private static final List<List<int[]>> DP3T_5pos_2502N_CONNECTIONS = Arrays.asList(
|
||||
Arrays.asList(
|
||||
new int[] { 0, 3 }
|
||||
), // position 1
|
||||
Arrays.asList(
|
||||
new int[] { 0, 3 },
|
||||
new int[] { 1, 3 },
|
||||
new int[] { 4, 7 },
|
||||
new int[] { 5, 7 }
|
||||
), // position 2
|
||||
Arrays.asList(
|
||||
new int[] { 0, 3 },
|
||||
new int[] { 2, 3 },
|
||||
new int[] { 6, 7 }
|
||||
), // position 3
|
||||
Arrays.asList(
|
||||
new int[] { 1, 3 },
|
||||
new int[] { 2, 3 },
|
||||
new int[] { 5, 7 }
|
||||
), // position 4
|
||||
Arrays.asList(
|
||||
new int[] { 2, 3 },
|
||||
new int[] { 6, 7 }
|
||||
) // position 5
|
||||
);
|
||||
|
||||
@DynamicEditableProperty(source = LeverSwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
@@ -559,7 +628,10 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
_4P5T("4P5T (Super/Mega)", 5),
|
||||
DP4T("DP4T (4-Position Tele)", 4),
|
||||
_6_WAY_OG("DP4T (6-Position Oak Grigsby)", 6),
|
||||
DP5T("DP5T", 5);
|
||||
DP5T("DP5T", 5),
|
||||
_6P5T("6P5T (5-Position 6 Pole AxLabs)", 5),
|
||||
_4P3T("4P3T (3-Position 4 Pole AxLabs)", 3),
|
||||
DP3T_5pos_2502N("DP3T (Import 5-Position 2502N)", 5);
|
||||
|
||||
private final int positionCount;
|
||||
private String title;
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "P-90 Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Single coil P-90 guitar pickup, both \"dog ear\" and \"soap bar\"",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true)
|
||||
public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "P- Bass Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Split-coil pickup for P-Bass and similar guitars",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "S1 Switch", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Fender S1 4 pole pushbutton switch", zOrder = IDIYComponent.COMPONENT,
|
||||
instanceNamePrefix = "SW", autoEdit = false, enableCache = true)
|
||||
instanceNamePrefix = "SW", enableCache = true)
|
||||
public class S1Switch extends AbstractTransparentComponent<Void> implements ISwitch {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -208,9 +208,9 @@ public class S1Switch extends AbstractTransparentComponent<Void> implements ISwi
|
||||
|
||||
double dy = 0;
|
||||
if (i % 3 == 1) {
|
||||
dy = -1;
|
||||
} else if (i % 3 == 0) {
|
||||
dy = 1;
|
||||
} else if (i % 3 == 0) {
|
||||
dy = -1;
|
||||
}
|
||||
|
||||
Stroke stroke = g2d.getStroke();
|
||||
@@ -280,9 +280,9 @@ public class S1Switch extends AbstractTransparentComponent<Void> implements ISwi
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
if (position == 0) {
|
||||
return "Up";
|
||||
return "Down";
|
||||
}
|
||||
return "Down";
|
||||
return "Up";
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
|
||||
@@ -54,7 +54,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic",
|
||||
description = "Single coil guitar pickup, both Strat and Tele style",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
|
||||
transformer = GuitarPickupTransformer.class)
|
||||
public class SingleCoilPickup extends AbstractSingleOrHumbuckerPickup {
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ import java.awt.geom.*;
|
||||
|
||||
@ComponentDescriptor(name = "Stingray Bass Pickup", category = "Guitar", author = "JD",
|
||||
description = "Humbucker Bass Pickup for Musicman Stingray and similar guitars",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
enableCache = true, transformer = GuitarPickupTransformer.class)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Auto-Wrap Label", author = "Branislav Stojkovic", category = "Misc",
|
||||
description = "User defined label with auto-wrapped text", instanceNamePrefix = "L", zOrder = IDIYComponent.TEXT, flexibleZOrder = true,
|
||||
description = "User defined label with auto-wrapped text", instanceNamePrefix = "LAB", zOrder = IDIYComponent.TEXT, flexibleZOrder = true,
|
||||
bomPolicy = BomPolicy.NEVER_SHOW, transformer = TextTransformer.class)
|
||||
public class WrapLabel extends AbstractComponent<String> {
|
||||
|
||||
|
||||
+75
@@ -35,9 +35,12 @@ 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.transform.PotentiometerTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.CreationMethod;
|
||||
@@ -66,10 +69,12 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
protected static Size PIN_SIZE = new Size(0.05d, SizeUnit.in);
|
||||
protected static Size NUT_SIZE = new Size(0.4d, SizeUnit.in);
|
||||
protected static Size SHAFT_SIZE = new Size(1 / 4d, SizeUnit.in);
|
||||
protected static Size MARKER_OFFSET = new Size(0.05d, SizeUnit.in);
|
||||
protected static Color BODY_COLOR = Color.lightGray;
|
||||
protected static Color WAFER_COLOR = Color.decode("#CD8500");
|
||||
protected static Color BORDER_COLOR = Color.gray;
|
||||
protected static Color NUT_COLOR = Color.decode("#CBD5DB");
|
||||
protected static Color MARKER_COLOR_DEFAULT = Color.gray;
|
||||
|
||||
|
||||
protected Size bodyDiameter = BODY_DIAMETER;
|
||||
@@ -83,6 +88,8 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
@Deprecated
|
||||
protected boolean showShaft = false;
|
||||
protected View view;
|
||||
private Boolean showMarkers;
|
||||
private Color markerColor;
|
||||
// Array of 7 elements: 3 lug connectors, 1 pot body and 3 lugs
|
||||
transient protected Area[] body = null;
|
||||
|
||||
@@ -353,6 +360,50 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
y = panelHeight / 2 + fontMetrics.getAscent();
|
||||
|
||||
g2d.drawString(getValueForDisplay(), (int) (bodyRect.getX() + x), (int) (bodyRect.getY() + y));
|
||||
|
||||
// Draw markers if enabled.
|
||||
if (getShowMarkers()) {
|
||||
g2d.setColor(outlineMode ? theme.getOutlineColor() : getMarkerColor());
|
||||
|
||||
// Get circle center and radius
|
||||
Rectangle2D circleBounds = body[3].getBounds2D();
|
||||
double centerX = circleBounds.getCenterX();
|
||||
double centerY = circleBounds.getCenterY();
|
||||
double radius = getBodyDiameter().convertToPixels() / 2;
|
||||
|
||||
// Fixed distance from circle edge (inside the circle)
|
||||
double markerOffset = MARKER_OFFSET.convertToPixels() + 2;
|
||||
double markerDistance = radius - markerOffset;
|
||||
|
||||
// Draw markers "1", "2", "3" along radial line from center to each lug, close to circle edge
|
||||
// Reverse numbering when view is ShaftDown
|
||||
boolean reverseOrder = getView() == View.ShaftDown;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Point2D lugPoint = controlPoints[i];
|
||||
|
||||
// Calculate direction from center to lug
|
||||
double dx = lugPoint.getX() - centerX;
|
||||
double dy = lugPoint.getY() - centerY;
|
||||
double distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0) {
|
||||
// Normalize direction vector
|
||||
double unitX = dx / distance;
|
||||
double unitY = dy / distance;
|
||||
|
||||
// Position marker at fixed distance from circle edge along radial line
|
||||
double markerX = centerX + unitX * markerDistance;
|
||||
double markerY = centerY + unitY * markerDistance;
|
||||
|
||||
// Determine marker number based on view
|
||||
int markerNumber = reverseOrder ? (3 - i) : (i + 1);
|
||||
|
||||
StringUtils.drawCenteredText(g2d, Integer.toString(markerNumber), markerX, markerY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -474,6 +525,30 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
public void setWaferColor(Color waferColor) {
|
||||
this.waferColor = waferColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
public Boolean getShowMarkers() {
|
||||
if (showMarkers == null) {
|
||||
showMarkers = false;
|
||||
}
|
||||
return showMarkers;
|
||||
}
|
||||
|
||||
public void setShowMarkers(Boolean showMarkers) {
|
||||
this.showMarkers = showMarkers;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Marker Color")
|
||||
public Color getMarkerColor() {
|
||||
if (markerColor == null) {
|
||||
markerColor = MARKER_COLOR_DEFAULT;
|
||||
}
|
||||
return markerColor;
|
||||
}
|
||||
|
||||
public void setMarkerColor(Color markerColor) {
|
||||
this.markerColor = markerColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInternalLinkName(int index1, int index2) {
|
||||
|
||||
@@ -157,6 +157,6 @@ public class TransformerCore extends AbstractComponent<Void> {
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+693
@@ -0,0 +1,693 @@
|
||||
/*
|
||||
|
||||
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.semiconductors;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
|
||||
import org.diylc.common.Display;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
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.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.gerber.IGerberComponentSimple;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Multiwatt IC", author = "Branislav Stojkovic", category = "Semiconductors",
|
||||
instanceNamePrefix = "IC", description = "Multiwatt package IC typically used for audio amplifiers of high power applications",
|
||||
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE,
|
||||
enableCache = true)
|
||||
public class MultiwattIC extends AbstractTransparentComponent<String> implements IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color BODY_COLOR = Color.gray;
|
||||
public static Color BORDER_COLOR = Color.gray.darker();
|
||||
public static Color PIN_COLOR = Color.decode("#00B2EE");
|
||||
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
public static Color LABEL_COLOR = Color.white;
|
||||
public static Color TAB_COLOR = Color.lightGray;
|
||||
public static Color TAB_BORDER_COLOR = TAB_COLOR.darker();
|
||||
public static int EDGE_RADIUS = 6;
|
||||
public static Size PIN_SIZE = new Size(0.7d, SizeUnit.mm);
|
||||
public static Size THICKNESS = new Size(5d, SizeUnit.mm);
|
||||
public static Size TAB_THICKNESS = new Size(1.6d, SizeUnit.mm);
|
||||
// Multiwatt packages use 0.1 inch (2.54mm) spacing between pins in the same row
|
||||
public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
|
||||
// Multiwatt packages have staggered pins with 0.2 inch (5.08mm) row spacing
|
||||
public static Size ROW_SPACING = new Size(0.2d, SizeUnit.in);
|
||||
// Lead offset due to bent leads
|
||||
public static Size LEAD_OFFSET = new Size(4d, SizeUnit.mm);
|
||||
|
||||
private String value = "";
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
private MultiwattType type = MultiwattType.MULTIWATT_11;
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
||||
protected Display display = Display.NAME;
|
||||
private Color bodyColor = BODY_COLOR;
|
||||
private Color borderColor = BORDER_COLOR;
|
||||
private Color labelColor = LABEL_COLOR;
|
||||
private Color tabColor = TAB_COLOR;
|
||||
private Color tabBorderColor = TAB_BORDER_COLOR;
|
||||
transient private Area[] body;
|
||||
|
||||
public MultiwattIC() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
alpha = 100;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Orientation getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
this.orientation = orientation;
|
||||
updateControlPoints();
|
||||
// Reset body shape.
|
||||
body = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Type")
|
||||
public MultiwattType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(MultiwattType type) {
|
||||
this.type = type;
|
||||
updateControlPoints();
|
||||
// Reset body shape;
|
||||
body = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Display getDisplay() {
|
||||
if (display == null) {
|
||||
display = Display.VALUE;
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
public void setDisplay(Display display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return controlPoints.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return controlPoints[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
controlPoints[index].setLocation(point);
|
||||
body = null;
|
||||
}
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
int pinCount = type.getPinCount();
|
||||
int pinsLeft = type.getPinsLeft();
|
||||
int pinsRight = type.getPinsRight();
|
||||
controlPoints = new Point2D.Double[pinCount];
|
||||
controlPoints[0] = firstPoint;
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
// Update control points for staggered layout
|
||||
double dx1, dy1, dx2, dy2;
|
||||
|
||||
// Left side pins (first row)
|
||||
for (int i = 0; i < pinsLeft; i++) {
|
||||
switch (orientation) {
|
||||
case DEFAULT:
|
||||
dx1 = 0;
|
||||
dy1 = i * pinSpacing;
|
||||
break;
|
||||
case _90:
|
||||
dx1 = -i * pinSpacing;
|
||||
dy1 = 0;
|
||||
break;
|
||||
case _180:
|
||||
dx1 = 0;
|
||||
dy1 = -i * pinSpacing;
|
||||
break;
|
||||
case _270:
|
||||
dx1 = i * pinSpacing;
|
||||
dy1 = 0;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
controlPoints[i] = new Point2D.Double((int) (firstPoint.getX() + dx1), (int) (firstPoint.getY() + dy1));
|
||||
}
|
||||
|
||||
// Right side pins (second row, staggered - offset by half pin spacing)
|
||||
for (int i = 0; i < pinsRight; i++) {
|
||||
switch (orientation) {
|
||||
case DEFAULT:
|
||||
dx2 = rowSpacing;
|
||||
dy2 = (i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
break;
|
||||
case _90:
|
||||
dx2 = -(i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
dy2 = rowSpacing;
|
||||
break;
|
||||
case _180:
|
||||
dx2 = -rowSpacing;
|
||||
dy2 = -(i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
break;
|
||||
case _270:
|
||||
dx2 = (i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
dy2 = -rowSpacing;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
controlPoints[i + pinsLeft] = new Point2D.Double((int) (firstPoint.getX() + dx2), (int) (firstPoint.getY() + dy2));
|
||||
}
|
||||
}
|
||||
|
||||
public Area[] getBody() {
|
||||
if (body == null) {
|
||||
body = new Area[2];
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
double pinSize = PIN_SIZE.convertToPixels();
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
double tabThickness = TAB_THICKNESS.convertToPixels();
|
||||
double leadOffset = LEAD_OFFSET.convertToPixels();
|
||||
int pinsLeft = type.getPinsLeft();
|
||||
int pinsRight = type.getPinsRight();
|
||||
// Calculate height accounting for staggered pins (right row offset by half spacing)
|
||||
double leftRowHeight = (pinsLeft - 1) * pinSpacing;
|
||||
double rightRowHeight = (pinsRight - 1 + 0.5) * pinSpacing; // Account for half-spacing offset
|
||||
double maxHeight = Math.max(leftRowHeight, rightRowHeight) + pinSpacing; // Add spacing for last pin
|
||||
double bodyX, bodyY, bodyWidth, bodyHeight;
|
||||
double tabX, tabY, tabWidth, tabHeight;
|
||||
|
||||
switch (orientation) {
|
||||
case DEFAULT:
|
||||
bodyWidth = rowSpacing - pinSize;
|
||||
bodyHeight = maxHeight;
|
||||
// Offset body to the left by lead offset (bent leads)
|
||||
bodyX = x + pinSize / 2 - leadOffset;
|
||||
bodyY = y - pinSpacing / 2;
|
||||
// Tab on the left side (where there are more pins) inside the body bounds
|
||||
tabX = bodyX;
|
||||
tabY = bodyY;
|
||||
tabWidth = tabThickness;
|
||||
tabHeight = bodyHeight;
|
||||
break;
|
||||
case _90:
|
||||
bodyWidth = maxHeight;
|
||||
bodyHeight = rowSpacing - pinSize;
|
||||
// Body positioned between the two pin rows
|
||||
// Left pins extend to negative X, body starts from leftmost pin
|
||||
// For _90: left pins go left (negative X), so leftmost is at x - (pinsLeft-1)*pinSpacing
|
||||
// Offset body upward by lead offset (bent leads)
|
||||
bodyX = x - (pinsLeft - 1) * pinSpacing - pinSpacing / 2;
|
||||
bodyY = y + pinSize / 2 - leadOffset;
|
||||
// Tab on the top side (where there are more pins - left row at y) inside the body bounds
|
||||
tabX = bodyX;
|
||||
tabY = bodyY;
|
||||
tabWidth = bodyWidth;
|
||||
tabHeight = tabThickness;
|
||||
break;
|
||||
case _180:
|
||||
bodyWidth = rowSpacing - pinSize;
|
||||
bodyHeight = maxHeight;
|
||||
// Body positioned between the two pin rows
|
||||
// For _180: left pins go up (negative Y), right pins at x - rowSpacing
|
||||
// Left row is at x, right row is at x - rowSpacing
|
||||
// Pins extend upward from y, so body should start from top of pin array
|
||||
// Offset body to the right by lead offset (bent leads)
|
||||
bodyX = x - rowSpacing + pinSize / 2 + leadOffset;
|
||||
bodyY = y - (pinsLeft - 1) * pinSpacing - pinSpacing / 2;
|
||||
// Tab on the right side (where there are more pins - left row at x) inside the body bounds
|
||||
tabX = bodyX + bodyWidth - tabThickness;
|
||||
tabY = bodyY;
|
||||
tabWidth = tabThickness;
|
||||
tabHeight = bodyHeight;
|
||||
break;
|
||||
case _270:
|
||||
bodyWidth = maxHeight;
|
||||
bodyHeight = rowSpacing - pinSize;
|
||||
// Body positioned between the two pin rows
|
||||
// For _270: left pins go right (positive X), so leftmost is at x
|
||||
// Left row is at y, right row is at y - rowSpacing
|
||||
// Similar to _90 but mirrored
|
||||
// Offset body downward by lead offset (bent leads)
|
||||
bodyX = x - pinSpacing / 2;
|
||||
bodyY = y - rowSpacing + pinSize / 2 + leadOffset;
|
||||
// Tab on the bottom side (where there are more pins - left row at y) inside the body bounds
|
||||
tabX = bodyX;
|
||||
tabY = bodyY + bodyHeight - tabThickness;
|
||||
tabWidth = bodyWidth;
|
||||
tabHeight = tabThickness;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
body[0] = new Area(new Rectangle2D.Double(bodyX, bodyY, bodyWidth, bodyHeight));
|
||||
body[1] = new Area(new Rectangle2D.Double(tabX, tabY, tabWidth, tabHeight));
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
Area mainArea = getBody()[0];
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
if (!outlineMode) {
|
||||
int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
|
||||
Rectangle2D bodyBounds = mainArea.getBounds2D();
|
||||
double leadThickness = pinSize; // Lead width matches pin size
|
||||
|
||||
for (Point2D point : controlPoints) {
|
||||
double pinX = point.getX();
|
||||
double pinY = point.getY();
|
||||
|
||||
// Check if pin is inside the body
|
||||
boolean insideBody = mainArea.contains(pinX, pinY);
|
||||
|
||||
if (insideBody) {
|
||||
// Draw pin as square inside body
|
||||
Rectangle2D rect = new Rectangle2D.Double(pinX - pinSize / 2,
|
||||
pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(rect);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.draw(rect);
|
||||
} else {
|
||||
// Draw rectangular lead from body edge to pin position
|
||||
double bodyX = bodyBounds.getX();
|
||||
double bodyY = bodyBounds.getY();
|
||||
double bodyWidth = bodyBounds.getWidth();
|
||||
double bodyHeight = bodyBounds.getHeight();
|
||||
|
||||
// Find the closest point on body edge to the pin
|
||||
double edgeX, edgeY;
|
||||
|
||||
// Determine which side of the body the pin is on
|
||||
if (pinX < bodyX) {
|
||||
// Pin is to the left of body
|
||||
edgeX = bodyX;
|
||||
edgeY = Math.max(bodyY, Math.min(bodyY + bodyHeight, pinY));
|
||||
} else if (pinX > bodyX + bodyWidth) {
|
||||
// Pin is to the right of body
|
||||
edgeX = bodyX + bodyWidth;
|
||||
edgeY = Math.max(bodyY, Math.min(bodyY + bodyHeight, pinY));
|
||||
} else if (pinY < bodyY) {
|
||||
// Pin is above body
|
||||
edgeX = Math.max(bodyX, Math.min(bodyX + bodyWidth, pinX));
|
||||
edgeY = bodyY;
|
||||
} else {
|
||||
// Pin is below body
|
||||
edgeX = Math.max(bodyX, Math.min(bodyX + bodyWidth, pinX));
|
||||
edgeY = bodyY + bodyHeight;
|
||||
}
|
||||
|
||||
// Draw rectangular lead
|
||||
double leadX = Math.min(edgeX, pinX) - leadThickness / 2;
|
||||
double leadY = Math.min(edgeY, pinY) - leadThickness / 2;
|
||||
double leadWidth = Math.abs(pinX - edgeX) + leadThickness;
|
||||
double leadHeight = Math.abs(pinY - edgeY) + leadThickness;
|
||||
|
||||
// If lead is horizontal or vertical, draw as rectangle
|
||||
if (Math.abs(pinX - edgeX) < 0.1) {
|
||||
// Vertical lead
|
||||
leadWidth = leadThickness;
|
||||
leadX = edgeX - leadThickness / 2;
|
||||
} else if (Math.abs(pinY - edgeY) < 0.1) {
|
||||
// Horizontal lead
|
||||
leadHeight = leadThickness;
|
||||
leadY = edgeY - leadThickness / 2;
|
||||
}
|
||||
|
||||
Rectangle2D leadRect = new Rectangle2D.Double(leadX, leadY, leadWidth, leadHeight);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(leadRect);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.draw(leadRect);
|
||||
|
||||
// Draw pin at the end of the lead
|
||||
Rectangle2D pinRect = new Rectangle2D.Double(pinX - pinSize / 2,
|
||||
pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(pinRect);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.draw(pinRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawingObserver.startTracking();
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
|
||||
g2d.fill(mainArea);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
// Draw heat sink tab
|
||||
Area tabArea = getBody()[1];
|
||||
Color finalTabColor;
|
||||
if (outlineMode) {
|
||||
Theme theme =
|
||||
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
finalTabColor = theme.getOutlineColor();
|
||||
} else {
|
||||
finalTabColor = tabColor;
|
||||
}
|
||||
g2d.setColor(finalTabColor);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(tabArea);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setComposite(oldComposite);
|
||||
|
||||
if (!outlineMode) {
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
g2d.setColor(tabBorderColor);
|
||||
g2d.draw(tabArea);
|
||||
}
|
||||
|
||||
Color finalBorderColor;
|
||||
if (outlineMode) {
|
||||
Theme theme =
|
||||
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: getBorderColor();
|
||||
}
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
g2d.draw(mainArea);
|
||||
// Draw label.
|
||||
g2d.setFont(project.getFont());
|
||||
Color finalLabelColor;
|
||||
if (outlineMode) {
|
||||
Theme theme =
|
||||
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
finalLabelColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalLabelColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
|
||||
: getLabelColor();
|
||||
}
|
||||
g2d.setColor(finalLabelColor);
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics(g2d.getFont());
|
||||
String label = "";
|
||||
label = (getDisplay() == Display.NAME) ? getName() : getValue();
|
||||
if (getDisplay() == Display.NONE) {
|
||||
label = "";
|
||||
}
|
||||
if (getDisplay() == Display.BOTH) {
|
||||
label = getName() + " " + (getValue() == null ? "" : getValue().toString());
|
||||
}
|
||||
Rectangle2D rect = fontMetrics.getStringBounds(label, g2d);
|
||||
int textHeight = (int) (rect.getHeight());
|
||||
int textWidth = (int) (rect.getWidth());
|
||||
// Center text horizontally and vertically
|
||||
Rectangle bounds = mainArea.getBounds();
|
||||
int x = (int) (bounds.getX() + (bounds.width - textWidth) / 2);
|
||||
int y = (int) (bounds.getY() + (bounds.height - textHeight) / 2 + fontMetrics.getAscent());
|
||||
g2d.drawString(label, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
int margin = 2;
|
||||
int pinSize = 2 * width / 32;
|
||||
int leftPinX = width / 6 + 9; // Move left pins 9px to the right (4px + 3px + 2px)
|
||||
int rightPinX = 5 * width / 6;
|
||||
int leadOffset = (int) (LEAD_OFFSET.convertToPixels() * width / 100); // Scale lead offset for icon
|
||||
int bodyX = leftPinX + pinSize - leadOffset + 2; // Offset body to the left, then move 2px to the right
|
||||
int bodyWidth = (rightPinX - bodyX - pinSize) / 2 + 2; // Thinner body, make 2px thicker
|
||||
int bodyHeight = height - 2 * margin;
|
||||
int bodyY = margin;
|
||||
|
||||
// Calculate pin spacing to use all vertical space
|
||||
// Use same spacing for both rows, with front row offset by half spacing
|
||||
int backPins = 4;
|
||||
int frontPins = 3;
|
||||
// Use the spacing that fits the back row (more pins)
|
||||
int pinSpacing = (bodyHeight - pinSize) / (backPins - 1);
|
||||
int backStartY = margin + pinSize / 2 + 1; // Move pins 1px down
|
||||
// Front pins start with half spacing offset for staggered effect
|
||||
int frontStartY = margin + pinSize / 2 + pinSpacing / 2 + 1; // Move pins 1px down
|
||||
|
||||
// Draw thin body between pin rows
|
||||
g2d.setColor(BODY_COLOR);
|
||||
g2d.fillRect(bodyX, bodyY, bodyWidth, bodyHeight);
|
||||
g2d.setColor(BORDER_COLOR);
|
||||
g2d.drawRect(bodyX, bodyY, bodyWidth, bodyHeight);
|
||||
|
||||
// Draw heat sink tab on left side (where there are more pins)
|
||||
int tabWidth = pinSize * 2;
|
||||
g2d.setColor(TAB_COLOR);
|
||||
g2d.fillRect(bodyX, bodyY, tabWidth, bodyHeight);
|
||||
g2d.setColor(TAB_BORDER_COLOR);
|
||||
g2d.drawRect(bodyX, bodyY, tabWidth, bodyHeight);
|
||||
|
||||
// Draw 4 pins in the back (left row, more pins) as squares - inside body
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < backPins; i++) {
|
||||
int pinY = backStartY + i * pinSpacing - pinSize / 2;
|
||||
g2d.fillRect(leftPinX - pinSize, pinY, pinSize, pinSize);
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.drawRect(leftPinX - pinSize, pinY, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
}
|
||||
|
||||
// Draw 3 pins in the front (right row, fewer pins) with leads going left to body
|
||||
// Use same spacing as back row, just offset by half spacing
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < frontPins; i++) {
|
||||
int pinY = frontStartY + i * pinSpacing;
|
||||
int pinX = rightPinX;
|
||||
int bodyRightEdge = bodyX + bodyWidth;
|
||||
|
||||
// Draw lead from body edge to pin (horizontal lead going left)
|
||||
if (pinX > bodyRightEdge) {
|
||||
int leadX = bodyRightEdge;
|
||||
int leadY = pinY - pinSize / 2;
|
||||
int leadWidth = pinX - bodyRightEdge;
|
||||
int leadHeight = pinSize;
|
||||
|
||||
g2d.fillRect(leadX, leadY, leadWidth, leadHeight);
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.drawRect(leadX, leadY, leadWidth, leadHeight);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
}
|
||||
|
||||
// Draw pin at the end of the lead
|
||||
g2d.fillRect(pinX - pinSize / 2, pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.drawRect(pinX - pinSize / 2, pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Body")
|
||||
public Color getBodyColor() {
|
||||
if (bodyColor == null) {
|
||||
bodyColor = BODY_COLOR;
|
||||
}
|
||||
return bodyColor;
|
||||
}
|
||||
|
||||
public void setBodyColor(Color bodyColor) {
|
||||
this.bodyColor = bodyColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Border")
|
||||
public Color getBorderColor() {
|
||||
if (borderColor == null) {
|
||||
borderColor = BORDER_COLOR;
|
||||
}
|
||||
return borderColor;
|
||||
}
|
||||
|
||||
public void setBorderColor(Color borderColor) {
|
||||
this.borderColor = borderColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Label")
|
||||
public Color getLabelColor() {
|
||||
if (labelColor == null) {
|
||||
labelColor = LABEL_COLOR;
|
||||
}
|
||||
return labelColor;
|
||||
}
|
||||
|
||||
public void setLabelColor(Color labelColor) {
|
||||
this.labelColor = labelColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Tab")
|
||||
public Color getTabColor() {
|
||||
if (tabColor == null) {
|
||||
tabColor = TAB_COLOR;
|
||||
}
|
||||
return tabColor;
|
||||
}
|
||||
|
||||
public void setTabColor(Color tabColor) {
|
||||
this.tabColor = tabColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Tab Border")
|
||||
public Color getTabBorderColor() {
|
||||
if (tabBorderColor == null) {
|
||||
tabBorderColor = TAB_BORDER_COLOR;
|
||||
}
|
||||
return tabBorderColor;
|
||||
}
|
||||
|
||||
public void setTabBorderColor(Color tabBorderColor) {
|
||||
this.tabBorderColor = tabBorderColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
double minX = Integer.MAX_VALUE;
|
||||
double maxX = Integer.MIN_VALUE;
|
||||
double minY = Integer.MAX_VALUE;
|
||||
double maxY = Integer.MIN_VALUE;
|
||||
int margin = 50;
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
if (p.getX() < minX)
|
||||
minX = p.getX();
|
||||
if (p.getX() > maxX)
|
||||
maxX = p.getX();
|
||||
if (p.getY() < minY)
|
||||
minY = p.getY();
|
||||
if (p.getY() > maxY)
|
||||
maxY = p.getY();
|
||||
}
|
||||
|
||||
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin);
|
||||
}
|
||||
|
||||
public static enum MultiwattType {
|
||||
// Multiwatt-11: 6 pins on left, 5 pins on right (staggered)
|
||||
MULTIWATT_11(11, 6, 5),
|
||||
// Multiwatt-15: 8 pins on left, 7 pins on right (staggered)
|
||||
MULTIWATT_15(15, 8, 7);
|
||||
|
||||
private final int pinCount;
|
||||
private final int pinsLeft;
|
||||
private final int pinsRight;
|
||||
|
||||
MultiwattType(int pinCount, int pinsLeft, int pinsRight) {
|
||||
this.pinCount = pinCount;
|
||||
this.pinsLeft = pinsLeft;
|
||||
this.pinsRight = pinsRight;
|
||||
}
|
||||
|
||||
public int getPinCount() {
|
||||
return pinCount;
|
||||
}
|
||||
|
||||
public int getPinsLeft() {
|
||||
return pinsLeft;
|
||||
}
|
||||
|
||||
public int getPinsRight() {
|
||||
return pinsRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Multiwatt " + pinCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.diylc.core.annotations.ComponentDescriptor;
|
||||
|
||||
@ComponentDescriptor(name = "Ellipse", author = "Branislav Stojkovic", category = "Shapes", instanceNamePrefix = "ELL",
|
||||
description = "Elliptical area", zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true,
|
||||
bomPolicy = BomPolicy.SHOW_ALL_NAMES, autoEdit = false, transformer = SimpleComponentTransformer.class)
|
||||
bomPolicy = BomPolicy.SHOW_ALL_NAMES, transformer = SimpleComponentTransformer.class)
|
||||
public class Ellipse extends AbstractShapeWithDimensions {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.diylc.core.annotations.EditableProperty;
|
||||
|
||||
@ComponentDescriptor(name = "Polygon", author = "Branislav Stojkovic", category = "Shapes",
|
||||
instanceNamePrefix = "POLY", description = "Polygonal area", zOrder = IDIYComponent.COMPONENT,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES, autoEdit = false,
|
||||
flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES,
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class Polygon extends AbstractShape {
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Rectangle", author = "Branislav Stojkovic", category = "Shapes",
|
||||
instanceNamePrefix = "RECT", description = "Ractangular area, with or withouth rounded edges",
|
||||
zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES, autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true, bomPolicy = BomPolicy.SHOW_ALL_NAMES,
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class Rectangle extends AbstractShapeWithDimensions {
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Tape Measure", author = "Branislav Stojkovic", category = "Misc",
|
||||
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "TM", description = "Measures distance between the two points",
|
||||
zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW,
|
||||
transformer = SimpleComponentTransformer.class)
|
||||
public class TapeMeasure extends AbstractLeadedComponent<String> {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class TapeMeasure extends AbstractLeadedComponent<String> {
|
||||
g2d.setColor(COLOR);
|
||||
g2d.drawLine(1, height - 2, width / 4, height * 3 / 4 - 1);
|
||||
g2d.drawLine(width * 3 / 4, height / 4 - 1, width - 2, 1);
|
||||
g2d.setBackground(Color.black);
|
||||
g2d.setColor(Color.black);
|
||||
g2d.fillPolygon(new int[] {1, 1, 4}, new int[] {height - 4, height - 1, height - 1}, 3);
|
||||
g2d.fillPolygon(new int[] {width - 4, width - 1, width - 1}, new int[] {1, 1, 4}, 3);
|
||||
g2d.rotate(-Math.PI / 4, width / 2, height / 2);
|
||||
@@ -174,6 +174,7 @@ public class TapeMeasure extends AbstractLeadedComponent<String> {
|
||||
g2d.rotate(theta);
|
||||
|
||||
g2d.setFont(font);
|
||||
g2d.setColor(componentState == ComponentState.SELECTED ? SELECTION_COLOR : labelColor);
|
||||
g2d.drawString(text, x, y);
|
||||
}
|
||||
|
||||
|
||||
+47
-10
@@ -27,18 +27,19 @@ import java.awt.geom.Point2D;
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.electromechanical.CliffJack1_4;
|
||||
import org.diylc.components.electromechanical.CliffJack1_8;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class CliffJackTransformer implements IComponentTransformer {
|
||||
|
||||
@Override
|
||||
public boolean canRotate(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(CliffJack1_4.class);
|
||||
return component.getClass().equals(CliffJack1_4.class) || component.getClass().equals(CliffJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMirror(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(CliffJack1_4.class);
|
||||
return component.getClass().equals(CliffJack1_4.class) || component.getClass().equals(CliffJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,8 +56,15 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
component.setControlPoint(p, index);
|
||||
}
|
||||
|
||||
CliffJack1_4 jack = (CliffJack1_4) component;
|
||||
Orientation o = jack.getOrientation();
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else if (component instanceof CliffJack1_8) {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
int oValue = o.ordinal();
|
||||
oValue += direction;
|
||||
if (oValue < 0)
|
||||
@@ -64,18 +72,33 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
if (oValue >= Orientation.values().length)
|
||||
oValue = 0;
|
||||
o = Orientation.values()[oValue];
|
||||
jack.setOrientation(o);
|
||||
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else if (component instanceof CliffJack1_8) {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
CliffJack1_4 ic = (CliffJack1_4) component;
|
||||
if (!(component instanceof CliffJack1_4) && !(component instanceof CliffJack1_8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDIYComponent<?> ic = component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - ic.getControlPoint(1).getX());
|
||||
double dy = 0;
|
||||
|
||||
Orientation o = ic.getOrientation();
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
case DEFAULT:
|
||||
dy += (ic.getControlPoint(0).getY() - ic.getControlPoint(ic.getControlPointCount() - 1).getY());
|
||||
@@ -98,12 +121,22 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i);
|
||||
}
|
||||
|
||||
ic.setOrientation(o);
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
} else {
|
||||
double dx = 0;
|
||||
double dy = 2 * (center.getY() - ic.getControlPoint(1).getY());
|
||||
|
||||
Orientation o = ic.getOrientation();
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
case DEFAULT:
|
||||
// dx += ic.getControlPoint(0).getX() - ic.getControlPoint(ic.getControlPointCount() - 1).getX();
|
||||
@@ -128,7 +161,11 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i);
|
||||
}
|
||||
|
||||
ic.setOrientation(o);
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -27,6 +27,7 @@ import java.awt.geom.Point2D;
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.common.OrientationHV;
|
||||
import org.diylc.components.electromechanical.MiniToggleSwitch;
|
||||
import org.diylc.components.electromechanical.ToggleSwitchType;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class MiniToggleSwitchTransformer implements IComponentTransformer {
|
||||
@@ -107,7 +108,8 @@ public class MiniToggleSwitchTransformer implements IComponentTransformer {
|
||||
MiniToggleSwitch sw = (MiniToggleSwitch) component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - sw.getControlPoint(1).getX());
|
||||
double dx = 2 * (center.getX() -
|
||||
(sw.getControlPoint(0).getX() + sw.getControlPoint(sw.getControlPointCount() - 1).getX()) / 2);
|
||||
double dy = 0;
|
||||
|
||||
for (int i = 0; i < sw.getControlPointCount(); i++) {
|
||||
@@ -116,7 +118,8 @@ public class MiniToggleSwitchTransformer implements IComponentTransformer {
|
||||
}
|
||||
} else {
|
||||
double dx = 0;
|
||||
double dy = 2 * (center.getY() - sw.getControlPoint(1).getY());
|
||||
double dy = 2 * (center.getY() -
|
||||
(sw.getControlPoint(0).getY() + sw.getControlPoint(sw.getControlPointCount() - 1).getY()) / 2);
|
||||
|
||||
for (int i = 0; i < sw.getControlPointCount(); i++) {
|
||||
Point2D p = sw.getControlPoint(i);
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
|
||||
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.transform;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.components.electromechanical.OpenJack1_4;
|
||||
import org.diylc.components.electromechanical.OpenJack1_8;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class OpenJackTransformer implements IComponentTransformer {
|
||||
|
||||
@Override
|
||||
public boolean canRotate(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(OpenJack1_4.class) || component.getClass().equals(OpenJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMirror(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(OpenJack1_4.class) || component.getClass().equals(OpenJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mirroringChangesCircuit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rotate(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2 * direction, center.getX(), center.getY());
|
||||
for (int index = 0; index < component.getControlPointCount(); index++) {
|
||||
Point2D p = new Point2D.Double();
|
||||
rotate.transform(component.getControlPoint(index), p);
|
||||
component.setControlPoint(p, index);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else if (component instanceof OpenJack1_8) {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
int newAngle = angle + direction * 90;
|
||||
// Normalize to 0-360 range
|
||||
while (newAngle < 0) {
|
||||
newAngle += 360;
|
||||
}
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else if (component instanceof OpenJack1_8) {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
if (!(component instanceof OpenJack1_4) && !(component instanceof OpenJack1_8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDIYComponent<?> ic = component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
// Mirror control points horizontally
|
||||
for (int i = 0; i < ic.getControlPointCount(); i++) {
|
||||
Point2D p = ic.getControlPoint(i);
|
||||
double dx = 2 * (center.getX() - p.getX());
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY()), i);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// Mirror horizontally: reflect angle across vertical axis
|
||||
int newAngle = 360 - angle;
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
} else {
|
||||
// Mirror control points vertically
|
||||
for (int i = 0; i < ic.getControlPointCount(); i++) {
|
||||
Point2D p = ic.getControlPoint(i);
|
||||
double dy = 2 * (center.getY() - p.getY());
|
||||
ic.setControlPoint(new Point2D.Double(p.getX(), p.getY() + dy), i);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// Mirror vertically: reflect angle across horizontal axis
|
||||
int newAngle = 180 - angle;
|
||||
while (newAngle < 0) {
|
||||
newAngle += 360;
|
||||
}
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -70,6 +70,20 @@ public class PinHeaderTransformer implements IComponentTransformer {
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
PinHeader header = (PinHeader) component;
|
||||
int controlPointCount = header.getControlPointCount();
|
||||
|
||||
// Handle single-pin case
|
||||
if (controlPointCount == 1) {
|
||||
Point2D p = header.getControlPoint(0);
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - p.getX());
|
||||
header.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY()), 0);
|
||||
} else {
|
||||
double dy = 2 * (center.getY() - p.getY());
|
||||
header.setControlPoint(new Point2D.Double(p.getX(), p.getY() + dy), 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - header.getControlPoint(1).getX());
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
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.awt;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class StringUtilsTest {
|
||||
|
||||
private FontMetrics fontMetrics;
|
||||
private Graphics2D g2d;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
|
||||
g2d = image.createGraphics();
|
||||
g2d.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
fontMetrics = g2d.getFontMetrics();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewline() {
|
||||
String text = "Line 1\nLine 2\nLine 3";
|
||||
int maxWidth = 1000; // Large enough to not wrap
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithCarriageReturn() {
|
||||
String text = "Line 1\rLine 2\rLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithCarriageReturnLineFeed() {
|
||||
String text = "Line 1\r\nLine 2\r\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMixedNewlines() {
|
||||
String text = "Line 1\nLine 2\rLine 3\r\nLine 4";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 4 lines", 4, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndBrTag() {
|
||||
String text = "Line 1\nLine 2<br>Line 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndWrapping() {
|
||||
String text = "First line\nSecond line with very long text that should wrap";
|
||||
int maxWidth = fontMetrics.stringWidth("Second line with very long text that should"); // Force wrapping
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertTrue("Should have at least 2 lines (newline + wrapped)", lines.size() >= 2);
|
||||
assertEquals("First line", lines.get(0));
|
||||
// The second line should be wrapped, so we should have more lines
|
||||
assertTrue("Second line should be wrapped", lines.size() >= 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithEmptyLines() {
|
||||
String text = "Line 1\n\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines including empty line", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAtEnd() {
|
||||
String text = "Line 1\nLine 2\n";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 2 lines (no trailing empty line)", 2, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNoNewlines() {
|
||||
String text = "This is a single line of text";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should have 1 line", 1, lines.size());
|
||||
assertEquals("This is a single line of text", lines.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithBrTagOnly() {
|
||||
String text = "Line 1<br>Line 2<br>Line 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndBrTagMixed() {
|
||||
String text = "Line 1\nLine 2<br>Line 3\nLine 4<br>Line 5";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 5 lines", 5, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
assertEquals("Line 5", lines.get(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlines() {
|
||||
String text = "Line 1\n\n\nLine 4";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 4 lines with 2 empty lines", 4, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesAtStart() {
|
||||
String text = "\n\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines with 2 empty lines at start", 3, lines.size());
|
||||
assertEquals("", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesAtEnd() {
|
||||
String text = "Line 1\n\n\n";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
// Trailing newlines: the last newline at end doesn't create an empty line
|
||||
// So \n\n\n at end creates 2 empty lines (from first 2 newlines)
|
||||
assertEquals("Should split into 3 lines (2 empty from trailing newlines)", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesMixed() {
|
||||
String text = "Line 1\n\nLine 3\n\n\nLine 6";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 6 lines", 6, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("", lines.get(3));
|
||||
assertEquals("", lines.get(4));
|
||||
assertEquals("Line 6", lines.get(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import org.diylc.components.guitar.Freeway3x3_05;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Freeway3x3_05Tests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testFreeway3x3_05() {
|
||||
Freeway3x3_05 sw = new Freeway3x3_05();
|
||||
|
||||
String[] validCombinations = new String[]{
|
||||
"0,0,6", "0,6,11", "0,0,11", "0,17,21", "0,21,22", "0,17,22",
|
||||
"1,1,6", "1,6,12", "1,1,12", "1,18,21", "1,21,23", "1,18,23",
|
||||
"2,2,6", "2,6,13", "2,2,13", "2,19,21", "2,21,24", "2,19,24",
|
||||
"3,5,6", "3,6,10", "3,5,10", "3,16,21", "3,21,27", "3,16,27",
|
||||
"4,4,6", "4,6,9", "4,4,9", "4,15,21", "4,21,26", "4,15,26",
|
||||
"5,3,6", "5,6,8", "5,3,8", "5,14,21", "5,21,25", "5,14,25",
|
||||
};
|
||||
|
||||
testSwitch(sw, validCombinations);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,117 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.diylc.components.guitar.LPSwitch;
|
||||
import org.diylc.components.guitar.LPSwitch.MiddleTerminalConfiguration;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class LPSwitchTests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testLPSwitch() {
|
||||
public void testLPSwitchConnected() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
// Default configuration is Connected
|
||||
assertEquals(MiddleTerminalConfiguration.Connected, lpSwitch.getMiddleTerminalConfiguration());
|
||||
// Connected has 4 control points (0, 1, 2, 3)
|
||||
assertEquals(4, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
// Position 0 (Treble): 1-2 connected
|
||||
// Position 1 (Middle): 1-2, 1-3, 2-3 connected (all terminals > 0 connected)
|
||||
// Position 2 (Rhythm): 2-3 connected
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,2,3", "1,1,3", "2,2,3"};
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLPSwitchDisconnected() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
assertEquals(MiddleTerminalConfiguration.Disconnected, lpSwitch.getMiddleTerminalConfiguration());
|
||||
// Disconnected has 5 control points (0, 1, 2, 3, 4)
|
||||
assertEquals(5, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
// Position 0 (Treble): 1-2 connected
|
||||
// Position 1 (Middle): 1-2 and 3-4 connected (both pairs)
|
||||
// Position 2 (Rhythm): 3-4 connected
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,3,4", "2,3,4"};
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlPointCountChange() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
// Start with Connected (4 points)
|
||||
assertEquals(4, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
|
||||
// Switch to Disconnected (5 points)
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
assertEquals(5, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
|
||||
// Switch back to Connected (4 points)
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Connected);
|
||||
assertEquals(4, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionNames() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
assertEquals(3, lpSwitch.getPositionCount());
|
||||
assertEquals("Treble", lpSwitch.getPositionName(0));
|
||||
assertEquals("Middle", lpSwitch.getPositionName(1));
|
||||
assertEquals("Rhythm", lpSwitch.getPositionName(2));
|
||||
assertNull(lpSwitch.getPositionName(3)); // Invalid position
|
||||
assertNull(lpSwitch.getPositionName(-1)); // Invalid position
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrientationDoesNotAffectConnectivity() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
// Test with default Connected configuration
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,2,3", "1,1,3", "2,2,3"};
|
||||
|
||||
// Test all orientations - connectivity should remain the same
|
||||
for (Orientation orientation : Orientation.values()) {
|
||||
lpSwitch.setOrientation(orientation);
|
||||
assertEquals(orientation, lpSwitch.getOrientation());
|
||||
// Connectivity should be unchanged regardless of orientation
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrientationWithDisconnectedConfiguration() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,3,4", "2,3,4"};
|
||||
|
||||
// Test all orientations with Disconnected configuration
|
||||
for (Orientation orientation : Orientation.values()) {
|
||||
lpSwitch.setOrientation(orientation);
|
||||
assertEquals(orientation, lpSwitch.getOrientation());
|
||||
// Connectivity should be unchanged regardless of orientation
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurationSwitchPreservesConnectivity() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
|
||||
// Test Connected configuration
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Connected);
|
||||
String[] connectedCombinations = new String[] {"0,1,2", "1,1,2", "1,2,3", "1,1,3", "2,2,3"};
|
||||
testSwitch(lpSwitch, connectedCombinations);
|
||||
|
||||
// Switch to Disconnected
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
String[] disconnectedCombinations = new String[] {"0,1,2", "1,1,2", "1,3,4", "2,3,4"};
|
||||
testSwitch(lpSwitch, disconnectedCombinations);
|
||||
|
||||
// Switch back to Connected
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Connected);
|
||||
testSwitch(lpSwitch, connectedCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,34 @@ public class LeverSwitchTests extends AbstractSwitchTests {
|
||||
leverSwitch.setValue(LeverSwitchType._4P5T);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitch6P5T() {
|
||||
LeverSwitch leverSwitch = new LeverSwitch();
|
||||
|
||||
String[] validCombinations = new String[] {
|
||||
"0,0,1", "0,6,11", "0,12,13", "0,18,23", "0,24,25", "0,30,35",
|
||||
"1,0,2", "1,7,11", "1,12,14", "1,19,23", "1,24,26", "1,31,35",
|
||||
"2,0,3", "2,8,11", "2,12,15", "2,20,23", "2,24,27", "2,32,35",
|
||||
"3,0,4", "3,9,11", "3,12,16", "3,21,23", "3,24,28", "3,33,35",
|
||||
"4,0,5", "4,10,11", "4,12,17", "4,22,23", "4,24,29", "4,34,35"
|
||||
};
|
||||
leverSwitch.setValue(LeverSwitchType._6P5T);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitch4P3T() {
|
||||
LeverSwitch leverSwitch = new LeverSwitch();
|
||||
|
||||
String[] validCombinations = new String[] {
|
||||
"0,0,1", "0,4,7", "0,8,9", "0,12,15",
|
||||
"1,0,2", "1,5,7", "1,8,10", "1,13,15",
|
||||
"2,0,3", "2,6,7", "2,8,11", "2,14,15",
|
||||
};
|
||||
leverSwitch.setValue(LeverSwitchType._4P3T);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitchImport() {
|
||||
@@ -50,4 +78,20 @@ public class LeverSwitchTests extends AbstractSwitchTests {
|
||||
leverSwitch.setValue(LeverSwitchType._6_WAY_OG);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeverSwitchImport2502N() {
|
||||
LeverSwitch leverSwitch = new LeverSwitch();
|
||||
|
||||
String[] validCombinations = new String[] {
|
||||
"0,0,3",
|
||||
"1,0,3", "1,1,3", "1,4,7", "1,5,7",
|
||||
"2,0,3", "2,2,3", "2,6,7",
|
||||
"3,1,3", "3,2,3", "3,5,7",
|
||||
"4,2,3", "4,6,7",
|
||||
};
|
||||
|
||||
leverSwitch.setValue(LeverSwitchType.DP3T_5pos_2502N);
|
||||
testSwitch(leverSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.diylc.components.electromechanical.TactileMicroSwitch;
|
||||
import org.diylc.components.electromechanical.TactileMicroSwitch.PinPairingMode;
|
||||
|
||||
public class TactileMicroSwitchTests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchOpposite() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Opposite);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): pins on opposite sides (same row): 0-2 and 1-3
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,2", "1,2,0", // Position 1: 0-2 (both directions)
|
||||
"1,1,3", "1,3,1" // Position 1: 1-3 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchDiagonal() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Diagonal);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): diagonally opposite pins: 0-3 and 1-2
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,3", "1,3,0", // Position 1: 0-3 (both directions)
|
||||
"1,1,2", "1,2,1" // Position 1: 1-2 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchAdjacent() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Adjacent);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): pins next to each other (same column): 0-1 and 2-3
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,1", "1,1,0", // Position 1: 0-1 (both directions)
|
||||
"1,2,3", "1,3,2" // Position 1: 2-3 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,58 @@
|
||||
#!/bin/bash
|
||||
#set -x # Enable debug mode
|
||||
set -x # Enable debug mode
|
||||
|
||||
# Get the AppImage directory
|
||||
if [ -n "$APPDIR" ]; then
|
||||
HERE="$APPDIR/usr/bin"
|
||||
JRE_PATH="$APPDIR/usr/lib/jre/bin/java"
|
||||
JRE_DIR="$APPDIR/usr/lib/jre"
|
||||
else
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
JRE_PATH="$(dirname "$HERE")/lib/jre/bin/java"
|
||||
JRE_DIR="$(dirname "$HERE")/lib/jre"
|
||||
fi
|
||||
|
||||
echo "diylc.sh: Running from directory: $HERE"
|
||||
echo "diylc.sh: Looking for JRE at: $JRE_PATH"
|
||||
|
||||
# Use system Java
|
||||
JAVA="$(which java)"
|
||||
if [ -z "$JAVA" ] || [ ! -x "$JAVA" ]; then
|
||||
echo "diylc.sh: ERROR - System Java not found or not executable!"
|
||||
echo "diylc.sh: Please install Java 17 or later"
|
||||
# List directory contents for debugging
|
||||
echo "diylc.sh: Contents of $(dirname "$JRE_PATH"):"
|
||||
ls -la "$(dirname "$JRE_PATH")"
|
||||
|
||||
# Use bundled JRE
|
||||
if [ ! -f "$JRE_PATH" ]; then
|
||||
echo "diylc.sh: ERROR - Bundled JRE not found at: $JRE_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "diylc.sh: Using system Java: $JAVA"
|
||||
# Check if we're running from a mounted AppImage
|
||||
if [[ "$HERE" == /tmp/.mount_* ]]; then
|
||||
echo "diylc.sh: Running from mounted AppImage"
|
||||
# Create a temporary directory for the JRE
|
||||
TEMP_DIR="/tmp/diylc-jre-$$"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
|
||||
# Copy the entire JRE directory to the temporary location
|
||||
echo "diylc.sh: Copying JRE to temporary location: $TEMP_DIR"
|
||||
cp -r "$JRE_DIR"/* "$TEMP_DIR/"
|
||||
chmod -R +x "$TEMP_DIR/bin"
|
||||
JRE_PATH="$TEMP_DIR/bin/java"
|
||||
|
||||
# Clean up the temporary directory on exit
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
fi
|
||||
|
||||
# Verify permissions
|
||||
echo "diylc.sh: Verifying JRE permissions..."
|
||||
ls -la "$JRE_PATH"
|
||||
|
||||
if [ ! -x "$JRE_PATH" ]; then
|
||||
echo "diylc.sh: ERROR - JRE is not executable at: $JRE_PATH"
|
||||
echo "diylc.sh: Current permissions: $(ls -l "$JRE_PATH")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "diylc.sh: Using JRE: $JRE_PATH"
|
||||
|
||||
cd "$HERE"
|
||||
echo "diylc.sh: Current working directory: $(pwd)"
|
||||
@@ -26,15 +60,17 @@ echo "diylc.sh: Current working directory: $(pwd)"
|
||||
# Debug: Print command before execution
|
||||
echo "diylc.sh: Executing DIYLCStarter with Java"
|
||||
|
||||
exec "$JAVA" \
|
||||
-Xms1024m \
|
||||
-Xmx4096m \
|
||||
-cp "$APPDIR/usr/lib/diylc.jar" \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens java.base/java.text=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.font=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.geom=ALL-UNNAMED \
|
||||
-Dorg.diylc.scriptRun=true \
|
||||
org.diylc.DIYLCStarter "$@"
|
||||
# Execute Java directly with the command array
|
||||
exec "$JRE_PATH" \
|
||||
-Xms1024m \
|
||||
-Xmx4096m \
|
||||
-cp "$(dirname "$HERE")/lib/diylc.jar" \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens java.base/java.text=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.font=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.geom=ALL-UNNAMED \
|
||||
-Dorg.diylc.scriptRun=true \
|
||||
org.diylc.DIYLCStarter "$@"
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ ChangesAssociations=yes
|
||||
AlwaysShowDirOnReadyPage=yes
|
||||
; Only allow the installer to run on x64-compatible systems,
|
||||
; and enable 64-bit install mode.
|
||||
ArchitecturesAllowed={#arch}
|
||||
ArchitecturesInstallIn64BitMode={#arch}
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
|
||||
[Files]
|
||||
Source: "diylc-{#arch}.exe"; DestDir: "{app}"; DestName: "diylc.exe"
|
||||
Source: "diylc-x64.exe"; DestDir: "{app}"; DestName: "diylc.exe"
|
||||
Source: "..\..\src\main\resources\icons\diylc_file.ico"; DestDir: "{app}"
|
||||
Source: "..\..\target\diylc.jar"; DestDir: "{app}"
|
||||
Source: "..\..\target\jre_win\*"; DestDir: "{app}\jre17"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
@@ -182,6 +182,7 @@ DP3T (Standard 5-Position Strat)
|
||||
DP4T (4-Position Tele)
|
||||
DP4T (6-Position Oak Grigsby)
|
||||
DP5T
|
||||
6P5T
|
||||
DPDT
|
||||
DPDT (On/Off/On)
|
||||
DPDT (On/On)
|
||||
|
||||
+64
-12
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-swing</artifactId>
|
||||
@@ -317,12 +317,49 @@
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Create AppDir structure and copy files -->
|
||||
<!-- Download and extract JRE -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>download-extract-jre</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<!-- Clean old jre folder -->
|
||||
<delete dir="${jre.dest.linux}" />
|
||||
|
||||
<!-- Create folder to hold extracted JRE -->
|
||||
<mkdir dir="${jre.dest.linux}" />
|
||||
|
||||
<!-- Download JRE for Linux -->
|
||||
<mkdir dir="${project.build.directory}/downloads" />
|
||||
<get src="${jdk.url.linux}"
|
||||
dest="${project.build.directory}/downloads/${jdk.archive.linux}"
|
||||
usetimestamp="true" />
|
||||
|
||||
<!-- Extract JRE -->
|
||||
<untar src="${project.build.directory}/downloads/${jdk.archive.linux}"
|
||||
dest="${jre.dest.linux}"
|
||||
compression="gzip" />
|
||||
|
||||
<!-- Move files up one level -->
|
||||
<move todir="${jre.dest.linux}">
|
||||
<fileset dir="${jre.dest.linux}/jdk-${jdk.version}-jre" />
|
||||
</move>
|
||||
|
||||
<!-- Remove empty top-level folder -->
|
||||
<delete dir="${jre.dest.linux}/jdk-${jdk.version}-jre" />
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<!-- Create AppDir structure and copy files -->
|
||||
<execution>
|
||||
<id>create-appdir</id>
|
||||
<phase>package</phase>
|
||||
@@ -339,7 +376,22 @@
|
||||
|
||||
<!-- Copy app files -->
|
||||
<copy file="${project.build.directory}/diylc.jar" todir="${appdir.dir}/usr/lib" />
|
||||
|
||||
|
||||
<!-- Copy JRE -->
|
||||
<copy todir="${appdir.dir}/usr/lib/jre">
|
||||
<fileset dir="${jre.dest.linux}" />
|
||||
</copy>
|
||||
|
||||
<!-- Make JRE files executable -->
|
||||
<chmod perm="755">
|
||||
<fileset dir="${appdir.dir}/usr/lib/jre/bin" includes="**/*" />
|
||||
</chmod>
|
||||
|
||||
<!-- Ensure JRE binaries are executable in the source directory too -->
|
||||
<chmod perm="755">
|
||||
<fileset dir="${jre.dest.linux}/bin" includes="**/*" />
|
||||
</chmod>
|
||||
|
||||
<!-- Copy AppImage specific files -->
|
||||
<copy file="${project.basedir}/deploy/linux/appimage/diylc.sh" tofile="${appdir.dir}/usr/bin/diylc.sh" />
|
||||
<copy file="${project.basedir}/deploy/linux/appimage/AppRun" tofile="${appdir.dir}/AppRun" />
|
||||
@@ -581,7 +633,7 @@
|
||||
<argument>${project.basedir}:/workspace</argument>
|
||||
<argument>amake/innosetup</argument>
|
||||
<argument>/Ddiylcver=${project.version}</argument>
|
||||
<argument>/Darch=x64</argument>
|
||||
<!--<argument>/Darch=win64 arm64</argument>-->
|
||||
<argument>/workspace/deploy/win/install.iss</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
@@ -839,11 +891,11 @@
|
||||
|
||||
<echo message="Creating unsigned zip for distribution..."/>
|
||||
<!-- Create unsigned zip for distribution -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-x86-unsigned.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-intel-unsigned.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
<zipfileset dir="${project.basedir}/deploy/mac" includes="README.txt" />
|
||||
</zip>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-x86-unsigned.zip"/>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-intel-unsigned.zip"/>
|
||||
|
||||
<!-- First remove existing signatures -->
|
||||
<echo message="Removing any existing signatures..."/>
|
||||
@@ -984,10 +1036,10 @@
|
||||
|
||||
<echo message="Creating final signed and notarized zip package..."/>
|
||||
<!-- Create final signed and notarized zip package -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-x86.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-intel.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
</zip>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-x86.zip"/>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-intel.zip"/>
|
||||
<echo message="Mac build process completed successfully!"/>
|
||||
</target>
|
||||
</configuration>
|
||||
@@ -1153,11 +1205,11 @@
|
||||
|
||||
<echo message="Creating unsigned zip for distribution..."/>
|
||||
<!-- Create unsigned zip for distribution -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-arm-unsigned.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-silicon-unsigned.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
<zipfileset dir="${project.basedir}/deploy/mac" includes="README.txt" />
|
||||
</zip>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-arm-unsigned.zip"/>
|
||||
<echo message="Unsigned zip created at: ${project.build.directory}/diylc-${project.version}-osx-silicon-unsigned.zip"/>
|
||||
|
||||
<!-- First remove existing signatures -->
|
||||
<echo message="Removing any existing signatures..."/>
|
||||
@@ -1298,10 +1350,10 @@
|
||||
|
||||
<echo message="Creating final signed and notarized zip package..."/>
|
||||
<!-- Create final signed and notarized zip package -->
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-arm.zip">
|
||||
<zip destfile="${project.build.directory}/diylc-${project.version}-osx-silicon.zip">
|
||||
<zipfileset filemode="755" dir="${project.build.directory}/DIYLC.app" prefix="DIYLC.app" />
|
||||
</zip>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-arm.zip"/>
|
||||
<echo message="Final package created at: ${project.build.directory}/diylc-${project.version}-osx-silicon.zip"/>
|
||||
<echo message="Mac build process completed successfully!"/>
|
||||
</target>
|
||||
</configuration>
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.diylc.serialization.ProjectFileManager;
|
||||
import org.diylc.swing.gui.MainFrame;
|
||||
import org.diylc.swing.gui.TemplateDialog;
|
||||
import org.diylc.swing.plugins.file.ProjectDrawingProvider;
|
||||
import org.diylc.utils.SwingUtils;
|
||||
|
||||
/**
|
||||
* Main class that runs DIYLC.
|
||||
@@ -137,6 +138,12 @@ public class DIYLCStarter {
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
|
||||
// Fix tooltip colors for Linux systems where they may be unreadable
|
||||
SwingUtils.fixTooltipColors();
|
||||
|
||||
// Apply configurable font scaling from VM argument: -Dorg.diylc.fontScaleFactor=X
|
||||
org.diylc.utils.FontScalingUtils.applyFontScaling();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Could not set Look&Feel", e);
|
||||
}
|
||||
|
||||
+3
-7
@@ -5,11 +5,7 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.swingframework.CheckBoxListDialog;
|
||||
@@ -69,7 +65,7 @@ public class ExportBlocksAction extends AbstractAction {
|
||||
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (dialog.getSelectedButtonCaption() != "OK")
|
||||
if (!Objects.equals(dialog.getSelectedButtonCaption(), "OK"))
|
||||
return;
|
||||
|
||||
Object[] selected = dialog.getSelectedOptions();
|
||||
@@ -133,4 +129,4 @@ public class ExportBlocksAction extends AbstractAction {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -5,13 +5,7 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.*;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.swingframework.CheckBoxListDialog;
|
||||
@@ -73,7 +67,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
ActionFactory.LOG.warn("Could not find type for: " + className);
|
||||
}
|
||||
|
||||
Collections.sort(types, new Comparator<ComponentType>() {
|
||||
types.sort(new Comparator<ComponentType>() {
|
||||
|
||||
@Override
|
||||
public int compare(ComponentType o1, ComponentType o2) {
|
||||
@@ -86,7 +80,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (dialog.getSelectedButtonCaption() != "OK")
|
||||
if (!Objects.equals(dialog.getSelectedButtonCaption(), "OK"))
|
||||
return;
|
||||
|
||||
Object[] selected = dialog.getSelectedOptions();
|
||||
@@ -131,7 +125,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
ProjectFileManager.xStreamSerializer.toXML(variantPkg, out);
|
||||
out.close();
|
||||
ActionFactory.LOG.info("Exported variants succesfully");
|
||||
ActionFactory.LOG.info("Exported variants successfully");
|
||||
} catch (IOException e) {
|
||||
ActionFactory.LOG.error("Could not export variants", e);
|
||||
}
|
||||
@@ -153,4 +147,4 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
|
||||
@Override
|
||||
public void injectGUIComponent(JComponent component, int position, boolean collapsible, String visibilityConfigKey) throws BadPositionException {
|
||||
LOG.info(String.format("injectGUIComponent(%s, %s, %s, %s)", component.getClass().getName(), position, collapsible, visibilityConfigKey));
|
||||
// LOG.info(String.format("injectGUIComponent(%s, %s, %s, %s)", component.getClass().getName(), position, collapsible, visibilityConfigKey));
|
||||
if (collapsible) {
|
||||
CollapsiblePanel panel = new CollapsiblePanel(position, visibilityConfigKey);
|
||||
panel.add(component);
|
||||
@@ -354,8 +354,8 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
|
||||
@Override
|
||||
public void injectMenuAction(Action action, String menuName) {
|
||||
LOG.info(String.format("injectMenuAction(%s, %s)", action == null ? "Separator" : action.getValue(Action.NAME),
|
||||
menuName));
|
||||
// LOG.info(String.format("injectMenuAction(%s, %s)", action == null ? "Separator" : action.getValue(Action.NAME),
|
||||
// menuName));
|
||||
if (menuName != null)
|
||||
menuName = LangUtil.translate(menuName);
|
||||
|
||||
|
||||
+43
-14
@@ -55,6 +55,21 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ActionBarPlugin.class);
|
||||
|
||||
private static final SupportOption[] SUPPORT_OPTIONS = new SupportOption[] {
|
||||
new SupportOption(
|
||||
"Enjoying DIYLC? Click here to buy me a coffee :)",
|
||||
"Donate",
|
||||
IconLoader.Donate,
|
||||
HelpMenuPlugin.DONATE_URL
|
||||
),
|
||||
new SupportOption(
|
||||
"Become a Patreon and enjoy perks",
|
||||
"Join Patreon",
|
||||
IconLoader.Patreon,
|
||||
HelpMenuPlugin.PATREON_URL
|
||||
)
|
||||
};
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private ISwingUI swingUI;
|
||||
|
||||
@@ -62,13 +77,12 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
private ActionToolbar contextActionToolbar;
|
||||
private ConfigToolbar configToolbar;
|
||||
private JLabel donateLabel;
|
||||
|
||||
private static final String DONATE_HTML = LangUtil.translate("Enjoying DIYLC? Click here to buy me a coffee :)");
|
||||
private static final String DONATE_HTML_SHORT = LangUtil.translate("Donate");
|
||||
private SupportOption selectedOption;
|
||||
private Consumer<Boolean> highlightConnectedAreasUpdateAction;
|
||||
|
||||
public ActionBarPlugin(ISwingUI swingUI) {
|
||||
this.swingUI = swingUI;
|
||||
this.selectedOption = SUPPORT_OPTIONS[(int)(Math.random() * SUPPORT_OPTIONS.length)];
|
||||
}
|
||||
|
||||
public JPanel getActionPanel() {
|
||||
@@ -110,12 +124,9 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
|
||||
public JLabel getDonateLabel() {
|
||||
if (donateLabel == null) {
|
||||
donateLabel = new JLabel(DONATE_HTML);
|
||||
// Map<TextAttribute, Integer> fontAttributes = new HashMap<TextAttribute, Integer>();
|
||||
// fontAttributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
||||
// donateLabel.setFont(donateLabel.getFont().deriveFont(fontAttributes));
|
||||
donateLabel = new JLabel(selectedOption.longText);
|
||||
donateLabel.setForeground(Color.blue);
|
||||
donateLabel.setIcon(IconLoader.Donate.getIcon());
|
||||
donateLabel.setIcon(selectedOption.icon.getIcon());
|
||||
donateLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
this.swingUI.getOwnerFrame().addComponentListener(new ComponentAdapter() {
|
||||
|
||||
@@ -126,11 +137,15 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
if (rootPane.getWidth() < 940) {
|
||||
if (!DONATE_HTML_SHORT.equals(donateLabel.getText()))
|
||||
donateLabel.setText(DONATE_HTML_SHORT);
|
||||
if (!selectedOption.shortText.equals(donateLabel.getText())) {
|
||||
donateLabel.setText(selectedOption.shortText);
|
||||
donateLabel.setIcon(selectedOption.icon.getIcon());
|
||||
}
|
||||
} else {
|
||||
if (!DONATE_HTML.equals(donateLabel.getText()))
|
||||
donateLabel.setText(DONATE_HTML);
|
||||
if (!selectedOption.longText.equals(donateLabel.getText())) {
|
||||
donateLabel.setText(selectedOption.longText);
|
||||
donateLabel.setIcon(selectedOption.icon.getIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -140,9 +155,11 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
try {
|
||||
Utils.openURL(HelpMenuPlugin.DONATE_URL);
|
||||
Utils.openURL(selectedOption.url);
|
||||
} catch (Exception e1) {
|
||||
swingUI.showMessage("Web browser launching failed. Please visit " + HelpMenuPlugin.DONATE_URL + " to donate. Thank you!", "Error", ISwingUI.INFORMATION_MESSAGE);
|
||||
swingUI.showMessage("Web browser launching failed. Please visit " +
|
||||
selectedOption.url +
|
||||
" to support DIYLC. Thank you!", "Error", ISwingUI.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -225,4 +242,16 @@ public class ActionBarPlugin implements IPlugIn {
|
||||
OperationMode.HIGHLIGHT_CONNECTED_AREAS);
|
||||
}
|
||||
}
|
||||
|
||||
private record SupportOption(
|
||||
String longText,
|
||||
String shortText,
|
||||
IconLoader icon,
|
||||
String url
|
||||
) {
|
||||
public SupportOption {
|
||||
longText = LangUtil.translate(longText);
|
||||
shortText = LangUtil.translate(shortText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class AutoSavePlugin implements IPlugIn {
|
||||
private static final Logger LOG = Logger.getLogger(AutoSavePlugin.class);
|
||||
|
||||
public static long BACKUP_FREQ_MS = 60 * 1000;
|
||||
public static int MAX_TOTAL_SIZE_MB = 64;
|
||||
public static int MAX_TOTAL_SIZE_MB = 1024;
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
|
||||
+12
-1
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
package org.diylc.swing.plugins.canvas;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.dnd.DragGestureEvent;
|
||||
import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.event.InputEvent;
|
||||
@@ -54,7 +55,17 @@ class CanvasDragGestureListener implements DragGestureListener {
|
||||
}
|
||||
canvasPanel.setClickInProgress(false);
|
||||
presenter.dragStarted(dge.getDragOrigin(), dge.getDragAction(), forceReSelection);
|
||||
dge.startDrag(presenter.getCursorAt(dge.getDragOrigin(), false, false, false), new EmptyTransferable(), new CanvasSourceListener(
|
||||
// Get the appropriate cursor from the presenter (e.g., HAND_CURSOR over components)
|
||||
// Preserve the cursor context (HAND_CURSOR, CROSSHAIR_CURSOR, etc.) for drag operations
|
||||
// On Linux, DEFAULT_CURSOR during drag can show "No" cursor, so use MOVE_CURSOR as fallback
|
||||
Cursor requestedCursor = presenter.getCursorAt(dge.getDragOrigin(), false, false, false);
|
||||
Cursor dragCursor = requestedCursor;
|
||||
// Only replace DEFAULT_CURSOR with MOVE_CURSOR on Linux to prevent "No" cursor issue
|
||||
// Preserve HAND_CURSOR, CROSSHAIR_CURSOR, etc. as they are appropriate for drag operations
|
||||
if (requestedCursor.getType() == Cursor.DEFAULT_CURSOR) {
|
||||
dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||
}
|
||||
dge.startDrag(dragCursor, new EmptyTransferable(), new CanvasSourceListener(
|
||||
presenter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,42 +146,35 @@ public class CanvasPanel extends JComponent implements Autoscroll {
|
||||
}
|
||||
|
||||
private void initializeActions() {
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_QUOTE, 0), "repeatLast");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "clearSlot");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, KeyEvent.META_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.META_DOWN_MASK), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, KeyEvent.CTRL_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, KeyEvent.META_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0), "zoomIn");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0), "zoomIn");
|
||||
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
|
||||
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, KeyEvent.CTRL_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, KeyEvent.CTRL_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, KeyEvent.META_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, KeyEvent.META_DOWN_MASK), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "resetZoom");
|
||||
getInputMap(JComponent.WHEN_FOCUSED)
|
||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "resetZoom");
|
||||
|
||||
for (int i = 1; i <= 12; i++) {
|
||||
final int x = i;
|
||||
@@ -198,36 +191,6 @@ public class CanvasPanel extends JComponent implements Autoscroll {
|
||||
});
|
||||
}
|
||||
|
||||
// getActionMap().put("zoomIn", new AbstractAction() {
|
||||
//
|
||||
// private static final long serialVersionUID = 1L;
|
||||
//
|
||||
// @Override
|
||||
// public void actionPerformed(ActionEvent e) {
|
||||
// LOG.debug("Keyboard zoom-in triggered");
|
||||
// double oldZoom = CanvasPanel.this.plugInPort.getZoomLevel();
|
||||
// Double[] availableZoomLevels = CanvasPanel.this.plugInPort.getAvailableZoomLevels();
|
||||
// int index = Arrays.binarySearch(availableZoomLevels, oldZoom);
|
||||
// if (index < availableZoomLevels.length - 1)
|
||||
// CanvasPanel.this.plugInPort.setZoomLevel(availableZoomLevels[index + 1]);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// getActionMap().put("zoomOut", new AbstractAction() {
|
||||
//
|
||||
// private static final long serialVersionUID = 1L;
|
||||
//
|
||||
// @Override
|
||||
// public void actionPerformed(ActionEvent e) {
|
||||
// LOG.debug("Keyboard zoom-out triggered");
|
||||
// double oldZoom = CanvasPanel.this.plugInPort.getZoomLevel();
|
||||
// Double[] availableZoomLevels = CanvasPanel.this.plugInPort.getAvailableZoomLevels();
|
||||
// int index = Arrays.binarySearch(availableZoomLevels, oldZoom);
|
||||
// if (index > 0)
|
||||
// CanvasPanel.this.plugInPort.setZoomLevel(availableZoomLevels[index - 1]);
|
||||
// }
|
||||
// });
|
||||
|
||||
getActionMap().put("resetZoom", new AbstractAction() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.apache.log4j.Logger;
|
||||
import org.diylc.appframework.miscutils.IConfigListener;
|
||||
import org.diylc.appframework.miscutils.IConfigurationManager;
|
||||
import org.diylc.appframework.miscutils.Utils;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.swingframework.ruler.IRulerListener;
|
||||
import org.diylc.swingframework.ruler.Ruler.InchSubdivision;
|
||||
import org.diylc.swingframework.ruler.RulerScrollPane;
|
||||
@@ -53,7 +54,6 @@ import org.diylc.common.BadPositionException;
|
||||
import org.diylc.common.EventType;
|
||||
import org.diylc.common.IPlugIn;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.presenter.Presenter;
|
||||
@@ -68,6 +68,8 @@ public class CanvasPlugin implements IPlugIn{
|
||||
private RulerScrollPane scrollPane;
|
||||
private CanvasPanel canvasPanel;
|
||||
private ComponentPopupMenu popupMenu;
|
||||
private ResizeInfoTooltip resizeInfoTooltip;
|
||||
private boolean resizingInProgress = false;
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private ISwingUI swingUI;
|
||||
@@ -193,6 +195,7 @@ public class CanvasPlugin implements IPlugIn{
|
||||
public CanvasPanel getCanvasPanel() {
|
||||
if (canvasPanel == null) {
|
||||
canvasPanel = new CanvasPanel(plugInPort, configManager);
|
||||
resizeInfoTooltip = new ResizeInfoTooltip(canvasPanel, configManager);
|
||||
canvasPanel.addMouseListener(new MouseAdapter() {
|
||||
|
||||
private MouseEvent pressedEvent;
|
||||
@@ -271,10 +274,21 @@ public class CanvasPlugin implements IPlugIn{
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
if (scrollPane.isMouseScrollMode())
|
||||
return;
|
||||
// if needed uncomment this code, it forces the cursor to refresh when it's stuck
|
||||
// canvasPanel.setCursor(null);
|
||||
canvasPanel.setCursor(plugInPort.getCursorAt(e.getPoint(), Utils.isMac() ? e.isMetaDown() : e.isControlDown(),
|
||||
e.isShiftDown(), e.isAltDown()));
|
||||
plugInPort.mouseMoved(e.getPoint(), Utils.isMac() ? e.isMetaDown() : e.isControlDown(),
|
||||
e.isShiftDown(), e.isAltDown());
|
||||
|
||||
// Update tooltip position if resizing is in progress and tooltip is enabled, otherwise hide it
|
||||
if (resizeInfoTooltip != null) {
|
||||
if (resizingInProgress && configManager.readBoolean(IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true)) {
|
||||
resizeInfoTooltip.updatePosition(e.getPoint());
|
||||
} else {
|
||||
resizeInfoTooltip.hideTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -503,11 +517,13 @@ public class CanvasPlugin implements IPlugIn{
|
||||
canvasPanel.repaint();
|
||||
// Refresh selection bounds after we're done with painting to ensure we have traced the
|
||||
// component areas
|
||||
boolean panToSelection = params.length > 0 && Boolean.TRUE.equals(params[0]);
|
||||
this.resizingInProgress = params.length > 1 && Boolean.TRUE.equals(params[1]);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (params.length > 0 && Boolean.TRUE.equals(params[0])) {
|
||||
if (panToSelection) {
|
||||
Rectangle2D selectionBounds = plugInPort.getSelectionBounds(true);
|
||||
if (selectionBounds != null) {
|
||||
Rectangle visibleRect = scrollPane.getVisibleRect();
|
||||
@@ -517,8 +533,28 @@ public class CanvasPlugin implements IPlugIn{
|
||||
);
|
||||
}
|
||||
}
|
||||
if (configManager.readBoolean(IPlugInPort.SHOW_RULERS_KEY, true))
|
||||
if (configManager.readBoolean(IPlugInPort.SHOW_RULERS_KEY, true)) {
|
||||
scrollPane.setSelectionRectangle(plugInPort.getSelectionBounds(true));
|
||||
}
|
||||
if (CanvasPlugin.this.resizingInProgress && getCanvasPanel().getMousePosition() != null) {
|
||||
// Only show tooltip if enabled in configuration
|
||||
if (configManager.readBoolean(IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true)) {
|
||||
ResizeDimensions resizeDimensions = plugInPort.getResizeDimensions();
|
||||
if (resizeInfoTooltip != null) {
|
||||
resizeInfoTooltip.update(resizeDimensions, getCanvasPanel().getMousePosition());
|
||||
}
|
||||
} else {
|
||||
// Hide tooltip if disabled
|
||||
if (resizeInfoTooltip != null) {
|
||||
resizeInfoTooltip.hideTooltip();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Hide tooltip when not resizing
|
||||
if (resizeInfoTooltip != null) {
|
||||
resizeInfoTooltip.hideTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -561,25 +597,4 @@ public class CanvasPlugin implements IPlugIn{
|
||||
double extraSpace = CanvasPlugin.this.plugInPort.getExtraSpace();
|
||||
getScrollPane().setZeroLocation(new Point2D.Double(extraSpace, extraSpace));
|
||||
}
|
||||
|
||||
class LockAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private IDIYComponent<?> component;
|
||||
private boolean locked;
|
||||
|
||||
public LockAction(IDIYComponent<?> component, boolean locked) {
|
||||
super();
|
||||
this.locked = locked;
|
||||
this.component = component;
|
||||
putValue(AbstractAction.NAME, component.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
plugInPort.lockComponent(component, locked);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+31
-2
@@ -21,11 +21,16 @@
|
||||
*/
|
||||
package org.diylc.swing.plugins.canvas;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Point;
|
||||
import java.awt.dnd.DragSourceDragEvent;
|
||||
import java.awt.dnd.DragSourceDropEvent;
|
||||
import java.awt.dnd.DragSourceEvent;
|
||||
import java.awt.dnd.DragSourceListener;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.diylc.common.IPlugInPort;
|
||||
|
||||
@@ -50,13 +55,16 @@ class CanvasSourceListener implements DragSourceListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragEnter(DragSourceDragEvent dsde) {}
|
||||
public void dragEnter(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragExit(DragSourceEvent dse) {}
|
||||
|
||||
@Override
|
||||
public void dragOver(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
Point p = dsde.getDragSourceContext().getComponent().getMousePosition();
|
||||
if (p != null) {
|
||||
dsde.getDragSourceContext().getComponent().firePropertyChange("dragPoint", p.x, p.y);
|
||||
@@ -64,5 +72,26 @@ class CanvasSourceListener implements DragSourceListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(DragSourceDragEvent dsde) {}
|
||||
public void dropActionChanged(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cursor during drag operations using the presenter as the single source of truth.
|
||||
* If the presenter returns DEFAULT_CURSOR, uses MOVE_CURSOR to prevent "No" cursor issues.
|
||||
*/
|
||||
private void updateCursor(DragSourceDragEvent dsde) {
|
||||
Point mousePos = dsde.getDragSourceContext().getComponent().getMousePosition();
|
||||
if (mousePos != null) {
|
||||
// Use presenter as single source of truth for cursor
|
||||
Cursor requestedCursor = presenter.getCursorAt(mousePos, false, false, false);
|
||||
Cursor dragCursor = requestedCursor;
|
||||
// If DEFAULT_CURSOR, use MOVE_CURSOR to prevent "No" cursor during drag operations
|
||||
if (requestedCursor.getType() == Cursor.DEFAULT_CURSOR) {
|
||||
dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||
}
|
||||
dsde.getDragSourceContext().setCursor(null);
|
||||
dsde.getDragSourceContext().setCursor(dragCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user