From ccf5d207131894b0abbf564b4be73ae9df81d920 Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Fri, 14 Mar 2025 11:23:56 +0100 Subject: [PATCH 01/32] mirror --- .../org/diylc/core/BoardUndersideDisplay.java | 10 + .../src/main/java/org/diylc/core/IBoard.java | 16 + .../org/diylc/core/gerber/GerberExporter.java | 84 +++-- .../org/diylc/core/gerber/IGerberBoard.java | 8 - .../org/diylc/presenter/DrawingCache.java | 24 +- .../org/diylc/presenter/DrawingManager.java | 355 ++++++++++++------ .../java/org/diylc/presenter/G2DWrapper.java | 170 ++++++--- .../components/boards/AbstractBoard.java | 194 ++++++---- .../diylc/components/boards/BlankBoard.java | 39 +- diylc/diylc-swing/dict.txt | 2 + 10 files changed, 581 insertions(+), 321 deletions(-) create mode 100644 diylc/diylc-core/src/main/java/org/diylc/core/BoardUndersideDisplay.java create mode 100644 diylc/diylc-core/src/main/java/org/diylc/core/IBoard.java delete mode 100644 diylc/diylc-core/src/main/java/org/diylc/core/gerber/IGerberBoard.java diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/BoardUndersideDisplay.java b/diylc/diylc-core/src/main/java/org/diylc/core/BoardUndersideDisplay.java new file mode 100644 index 00000000..1c402bb0 --- /dev/null +++ b/diylc/diylc-core/src/main/java/org/diylc/core/BoardUndersideDisplay.java @@ -0,0 +1,10 @@ +package org.diylc.core; + +public enum BoardUndersideDisplay { + NONE, ABOVE, BELOW, LEFT, RIGHT; + + @Override + public String toString() { + return super.toString().charAt(0) + super.toString().substring(1).toLowerCase(); + } +} diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/IBoard.java b/diylc/diylc-core/src/main/java/org/diylc/core/IBoard.java new file mode 100644 index 00000000..302547d3 --- /dev/null +++ b/diylc/diylc-core/src/main/java/org/diylc/core/IBoard.java @@ -0,0 +1,16 @@ +package org.diylc.core; + +import org.diylc.core.measures.Size; + +import java.awt.geom.Rectangle2D; + +public interface IBoard { + + Rectangle2D getBoardRectangle(); + + BoardUndersideDisplay getBoardUndersideDisplay(); + + Size getUndersideOffset(); + + boolean shouldExportToGerber(); +} diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java index 066254c7..916d904a 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java +++ b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java @@ -13,16 +13,15 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.log4j.Logger; - -import com.bancika.gerberwriter.DataLayer; - import org.diylc.core.ComponentState; +import org.diylc.core.IBoard; import org.diylc.core.IDIYComponent; import org.diylc.core.IView; import org.diylc.core.Project; import org.diylc.core.measures.Size; import org.diylc.core.measures.SizeUnit; import org.diylc.lang.LangUtil; +import com.bancika.gerberwriter.DataLayer; public class GerberExporter { @@ -30,44 +29,47 @@ public class GerberExporter { private static final Logger LOG = Logger.getLogger(GerberExporter.class); public static void exportGerber(String fileNameBase, Project currentProject, IView view, - Graphics2D graphics2d, String diylcVersion) { - List gerberBoards = - currentProject.getComponents().stream().filter(c -> c instanceof IGerberBoard) - .map(x -> (IGerberBoard) x).collect(Collectors.toList()); + Graphics2D graphics2d, String diylcVersion) { - Map>> boardComponentMap = - new HashMap>>(); + List gerberBoards = + currentProject.getComponents().stream() + .filter(IBoard.class::isInstance) + .map(IBoard.class::cast) + .filter(IBoard::shouldExportToGerber).collect(Collectors.toList()); + + Map>> boardComponentMap = + new HashMap>>(); // DefaultGerberBoard defaultGerberBoard = new DefaultGerberBoard(); List> componentsWithoutBoard = new ArrayList>(); currentProject.getComponents().stream().filter(c -> c instanceof IGerberComponent) - .forEach(c -> { - Point2D firstPoint = c.getControlPoint(0); - IGerberBoard board = gerberBoards.stream() - .filter(b -> b.getBoardRectangle().contains(firstPoint)).findFirst().orElse(null); - if (board == null) { - componentsWithoutBoard.add(c); - } else { - boardComponentMap.computeIfAbsent(board, k -> new ArrayList>()).add(c); - } - }); + .forEach(c -> { + Point2D firstPoint = c.getControlPoint(0); + IBoard board = gerberBoards.stream() + .filter(b -> b.getBoardRectangle().contains(firstPoint)).findFirst().orElse(null); + if (board == null) { + componentsWithoutBoard.add(c); + } else { + boardComponentMap.computeIfAbsent(board, k -> new ArrayList>()).add(c); + } + }); // defaultGerberBoard.setComponents(boardComponentMap.get(defaultGerberBoard)); if (boardComponentMap.isEmpty()) { view.showMessage(LangUtil.translate("Nothing to export."), - LangUtil.translate("Gerber Export"), IView.WARNING_MESSAGE); + LangUtil.translate("Gerber Export"), IView.WARNING_MESSAGE); return; } if (componentsWithoutBoard.size() > 0) { if (view.showConfirmDialog(String.format( - LangUtil.translate("There are some components that are outside of the bounds of boards.%s" - + "They will be ignored unless you add a board component underneath them.%sDo you want to continue?"), - "\n", "\n"), LangUtil.translate("Gerber Export"), IView.YES_NO_OPTION, - IView.WARNING_MESSAGE) != IView.YES_OPTION) { + LangUtil.translate("There are some components that are outside of the bounds of boards.%s" + + "They will be ignored unless you add a board component underneath them.%sDo you want to continue?"), + "\n", "\n"), LangUtil.translate("Gerber Export"), IView.YES_NO_OPTION, + IView.WARNING_MESSAGE) != IView.YES_OPTION) { return; } } @@ -88,15 +90,15 @@ public class GerberExporter { String fileNameOutline = GerberLayer.Outline.formatFileName(fileNameBase, boardComponent.getName()); try { LOG.info(String.format(LangUtil.translate("Exporting outline for board %s to file %s"), - boardComponent.getName(), fileNameOutline)); + boardComponent.getName(), fileNameOutline)); outlineLayer.dumpGerberToFile(fileNameOutline); generatedFiles.add(fileNameOutline); } catch (IOException e) { LOG.error("Error writing gerber file: " + e.getMessage()); view.showMessage( - LangUtil.translate( - "Failed to export the project to gerber. Please check the log for details"), - LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE); + LangUtil.translate( + "Failed to export the project to gerber. Please check the log for details"), + LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE); return; } @@ -119,30 +121,30 @@ public class GerberExporter { try { String fileName = layer.formatFileName(fileNameBase, boardComponent.getName()); LOG.info("Exporting layer: " + layer + " for board " + boardComponent.getName() - + " to file: " + fileName); + + " to file: " + fileName); dataLayer.dumpGerberToFile(fileName); generatedFiles.add(fileName); } catch (IOException e) { LOG.error("Error writing gerber file: " + e.getMessage()); view.showMessage( - LangUtil.translate( - "Failed to export the project to gerber. Please check the log for details"), - LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE); + LangUtil.translate( + "Failed to export the project to gerber. Please check the log for details"), + LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE); return; } }); }); view.showMessage( - String.format( - LangUtil.translate( - "Gerber export completed successfully. The following files were created:%s"), - "\n\n" + String.join("\n", generatedFiles)), - LangUtil.translate("Gerber Export"), IView.INFORMATION_MESSAGE); + String.format( + LangUtil.translate( + "Gerber export completed successfully. The following files were created:%s"), + "\n\n" + String.join("\n", generatedFiles)), + LangUtil.translate("Gerber Export"), IView.INFORMATION_MESSAGE); LOG.info("Completed export to gerber"); } - + private static void drawComponent(Project currentProject, GerberG2DWrapper g2d, - IDIYComponent c, boolean outlineMode) { + IDIYComponent c, boolean outlineMode) { IGerberComponent gerberComponent = (IGerberComponent) c; g2d.startedDrawingComponent(); if (gerberComponent instanceof IGerberComponentCustom) { @@ -151,9 +153,9 @@ public class GerberExporter { } else if (gerberComponent instanceof IGerberComponentSimple) { IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent; g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(), - gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative()); + gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative()); c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d); g2d.stopGerberOutput(); } } -} +} \ No newline at end of file diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/IGerberBoard.java b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/IGerberBoard.java deleted file mode 100644 index 76474d71..00000000 --- a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/IGerberBoard.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.diylc.core.gerber; - -import java.awt.geom.Rectangle2D; - -public interface IGerberBoard { - - Rectangle2D getBoardRectangle(); -} diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java index e0aacfe2..798c7d7b 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java @@ -74,7 +74,7 @@ public class DrawingCache { .extractComponentTypeFrom((Class>) 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(), false); } }); } @@ -89,10 +89,14 @@ public class DrawingCache { * @param outlineMode * @param project * @param zoom + * @param scaleFactor + * @param zOrder + * @param mirrored */ @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, + boolean mirrored) { ComponentType type = ComponentProcessor.getInstance() .extractComponentTypeFrom((Class>) component.getClass()); @@ -100,7 +104,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, mirrored); if (value == null) { return; @@ -180,7 +184,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, boolean mirrored) { // if we need to apply caching Point2D firstPoint = component.getControlPoint(0); @@ -196,7 +200,7 @@ public class DrawingCache { // only honor the cache if the component hasn't changed in the meantime if (value == null || !value.getComponent().equalsTo(component) || value.getState() != componentState || value.getZoom() != zoom || value.getOutlineMode() != outlineMode - || value.getScaleFactor() != scaleFactor) { + || value.getScaleFactor() != scaleFactor || value.isMirrored() != mirrored) { // figure out size and placement of buffer image Rectangle2D rect = component.getCachingBounds(); int width = (int)Math.round(rect.getWidth() * zoom); @@ -270,7 +274,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, mirrored); imageCache.put(component, new SoftReference(value)); } @@ -287,6 +291,7 @@ public class DrawingCache { boolean outlineMode; double zoom; double scaleFactor; + boolean mirrored; int dx; int dy; Rectangle2D cacheBounds; @@ -294,7 +299,7 @@ public class DrawingCache { public CacheValue(IDIYComponent component, BufferedImage image, Area currentArea, Map continuityPositiveAreas, Map continuityNegativeAreas, ComponentState state, boolean outlineMode, double zoom, double scaleFactor, int dx, int dy, - Rectangle2D cacheBounds) { + Rectangle2D cacheBounds, boolean mirrored) { super(); this.cacheBounds = cacheBounds; // take a copy of the component, so we can check if it changed in the meantime @@ -309,6 +314,7 @@ public class DrawingCache { this.state = state; this.outlineMode = outlineMode; this.zoom = zoom; + this.scaleFactor = scaleFactor; this.dx = dx; this.dy = dy; } @@ -349,6 +355,10 @@ public class DrawingCache { return scaleFactor; } + public boolean isMirrored() { + return mirrored; + } + public int getDx() { return dx; } diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java index f2920b1d..d14078e4 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java @@ -1,17 +1,17 @@ /* - * + * * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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 * . */ @@ -46,17 +46,20 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.log4j.Logger; +import org.checkerframework.checker.units.qual.A; import org.diylc.appframework.miscutils.IConfigurationManager; import org.diylc.appframework.simplemq.MessageDispatcher; - import org.diylc.common.DrawOption; import org.diylc.common.EventType; import org.diylc.common.GridType; import org.diylc.common.IComponentFilter; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; +import org.diylc.components.AbstractTransparentComponent; +import org.diylc.core.BoardUndersideDisplay; import org.diylc.core.ComponentForRender; import org.diylc.core.ComponentState; +import org.diylc.core.IBoard; import org.diylc.core.IContinuity; import org.diylc.core.IDIYComponent; import org.diylc.core.ILayeredComponent; @@ -72,7 +75,7 @@ import org.diylc.utils.Constants; /** * Utility that deals with painting {@link Project} on the {@link Graphics2D} and keeps areas taken * by each drawn component. - * + * * @author Branislav Stojkovic */ public class DrawingManager { @@ -92,6 +95,8 @@ public class DrawingManager { public static Color CONTROL_POINT_COLOR = Color.blue; public static Color SELECTED_CONTROL_POINT_COLOR = Color.green; + private static final Byte MIRRORED_ALPHA = 64; + private Theme theme; // Maps keyed by object reference. @@ -127,7 +132,7 @@ public class DrawingManager { private List proximityMarkers = null; public DrawingManager(MessageDispatcher messageDispatcher, - IConfigurationManager configManager) { + IConfigurationManager configManager) { super(); this.messageDispatcher = messageDispatcher; this.configManager = configManager; @@ -145,16 +150,16 @@ public class DrawingManager { lastDrawnStateMap = new HashMap, ComponentState>(); String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS); debugComponentAreas = - debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true"); + debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true"); String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS); debugContinuityAreas = - debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); + debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); } /** * Paints the project onto the canvas and returns the list of components that failed to draw. - * + * * @param g2d * @param project * @param drawOptions @@ -167,16 +172,18 @@ public class DrawingManager { * @param componentSlot * @param dragInProgress * @param externalZoom - * @param scaleFactor + * @param scaleFactor * @param visibleRect * @return */ + @SuppressWarnings("unlikely-arg-type") public List> drawProject(Graphics2D g2d, Project project, - Set drawOptions, IComponentFilter filter, Rectangle selectionRect, - Collection> selectedComponents, Set> lockedComponents, - Set> groupedComponents, List controlPointSlot, - List> componentSlot, boolean dragInProgress, Double externalZoom, - Double scaleFactor, Rectangle2D visibleRect) { + Set drawOptions, IComponentFilter filter, Rectangle selectionRect, + Collection> selectedComponents, Set> lockedComponents, + Set> groupedComponents, List controlPointSlot, + List> componentSlot, boolean dragInProgress, Double externalZoom, + Double scaleFactor, Rectangle2D visibleRect) { + long totalStartTime = System.nanoTime(); failedComponents.clear(); if (project == null) { @@ -195,42 +202,39 @@ public class DrawingManager { G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom); - if (drawOptions.contains(DrawOption.ANTIALIASING)) { - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - } else { - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + configureG2D(g2d, drawOptions); + + // group components with boards that contain them + Map>> boardMap = project.getComponents().stream() + .filter(IBoard.class::isInstance) + .map(IBoard.class::cast) + .filter(b -> b.getBoardUndersideDisplay() != BoardUndersideDisplay.NONE) + .collect(Collectors.toMap(x -> x, x -> new ArrayList>())); + Map, List> componentBoardMap = new HashMap, List>(); + + for (IBoard board : boardMap.keySet()) { + Rectangle2D boardRect = board.getBoardRectangle(); + for (IDIYComponent c : project.getComponents()) { + if (c == board) { + continue; + } + boolean include = true; + for (int i = 0; i < c.getControlPointCount(); i++) { + if (!boardRect.contains(c.getControlPoint(i))) { + include = false; + break; + } + } + if (include) { + boardMap.get(board).add(c); + componentBoardMap.computeIfAbsent(c, key -> new ArrayList()).add(board); + } + } } - if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) { - g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, - RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - // g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, - // RenderingHints.VALUE_STROKE_PURE); - } else { - g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, - RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); - g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_SPEED); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); - g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - } - g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_OFF); // AffineTransform initialTx = g2d.getTransform(); Dimension canvasDimension = - getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE)); + getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE)); g2dWrapper.setColor(theme.getBgColor()); g2dWrapper.fillRect(0, 0, canvasDimension.width, canvasDimension.height); @@ -243,14 +247,14 @@ public class DrawingManager { double extraSpace = getExtraSpace(project) * zoom; drawGrid(project, g2d, zoom, extraSpace, canvasDimension, visibleRect, drawOptions, - extraSpaceRect, extraSpaceTx); + extraSpaceRect, extraSpaceTx); // apply zoom if (Math.abs(1.0 - zoom) > 1e-4) { g2dWrapper.scale(zoom, zoom); if (visibleRect != null) visibleRect.setRect(visibleRect.getX() / zoom, visibleRect.getY() / zoom, - visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); + visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); } // Composite mainComposite = g2d.getComposite(); @@ -263,7 +267,7 @@ public class DrawingManager { Map, ComponentState> componentStateMap = new HashMap, ComponentState>(); List componentForRenderList = new ArrayList(); for (int i = 0; i < project.getComponents().size(); i++) { - IDIYComponent component = project.getComponents().get(i); + IDIYComponent component = project.getComponents().get(i); ComponentState state = ComponentState.NORMAL; if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) { if (dragInProgress) { @@ -287,17 +291,18 @@ public class DrawingManager { for (int i = 0; i < project.getComponents().size(); i++) { IDIYComponent component = project.getComponents().get(i); - + // Do not draw the component if it's filtered out. if (filter != null && !filter.testComponent(component)) { continue; } + ComponentState state = componentStateMap.get(component); // Do not track the area if component is not invalidated and was // drawn in the same state. boolean trackArea = lastDrawnStateMap.get(component) != state; - synchronized (g2d) { + synchronized (this) { g2dWrapper.startedDrawingComponent(i); if (!trackArea) { g2dWrapper.stopTracking(); @@ -312,15 +317,26 @@ public class DrawingManager { long componentStart = System.nanoTime(); try { - 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); - else // go straight to the wrapper - component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); + AffineTransform txBefore = new AffineTransform(g2d.getTransform()); + drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper, + outlineMode, i, component, state, false); + g2dWrapper.setTransform(txBefore); - if (g2dWrapper.isTrackingContinuityArea()) { - LOG.info("Component " + component.getName() + " of type " - + component.getClass().getName() + " did not stop tracking continuity area."); + // find one or more boards that contain the component and mirror the component to all + List boards = componentBoardMap.get(component); + if (boards != null) { + for (IBoard board : boards) { + double boardMirrorSpacing = board.getUndersideOffset().convertToPixels(); + drawMirroredComponent(project, drawOptions, scaleFactor, visibleRect, zoom, + g2dWrapper, outlineMode, i, state, boardMirrorSpacing, component, board); + } + } + // if the component is the board itself, mirror it + if (boardMap.keySet().contains(component)) { + IBoard board = (IBoard) component; + double boardMirrorSpacing = board.getUndersideOffset().convertToPixels(); + drawMirroredComponent(project, drawOptions, scaleFactor, visibleRect, zoom, + g2dWrapper, outlineMode, i, state, boardMirrorSpacing, component, (IBoard)component); } } catch (Exception e) { LOG.error("Error drawing " + component.getName(), e); @@ -362,17 +378,17 @@ public class DrawingManager { continue; VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i); if ((groupedComponents.contains(component) - && (visibilityPolicy == VisibilityPolicy.ALWAYS + && (visibilityPolicy == VisibilityPolicy.ALWAYS || (selectedComponents.contains(component) - && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) - || (!groupedComponents.contains(component) + && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) + || (!groupedComponents.contains(component) && !selectedComponents.contains(component) && component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) { g2dWrapper.setColor(CONTROL_POINT_COLOR); Point2D controlPoint = component.getControlPoint(i); int pointSize = CONTROL_POINT_SIZE - 2; g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2), - (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); + (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); } } } @@ -381,7 +397,7 @@ public class DrawingManager { for (IDIYComponent component : selectedComponents) { for (int i = 0; i < component.getControlPointCount(); i++) { if (!groupedComponents.contains(component) - && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED + && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED || component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) { Point2D controlPoint = component.getControlPoint(i); @@ -389,11 +405,11 @@ public class DrawingManager { g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2), - (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); + (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1), - (int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, - CONTROL_POINT_SIZE - 2); + (int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, + CONTROL_POINT_SIZE - 2); } } } @@ -407,7 +423,7 @@ public class DrawingManager { try { component.draw(g2dWrapper, ComponentState.NORMAL, - drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); + drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); } catch (Exception e) { LOG.error("Error drawing " + component.getName(), e); @@ -423,12 +439,12 @@ public class DrawingManager { if (point != null) { g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2), - (int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE, - CONTROL_POINT_SIZE); + (int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE, + CONTROL_POINT_SIZE); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1), - (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, - CONTROL_POINT_SIZE - 2); + (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, + CONTROL_POINT_SIZE - 2); } } } @@ -493,7 +509,7 @@ public class DrawingManager { // shade extra space if (drawOptions.contains(DrawOption.EXTRA_SPACE) && SHADE_EXTRA_SPACE && extraSpaceRect != null) { Area extraSpaceArea = new Area( - new Rectangle2D.Double(0, 0, canvasDimension.getWidth(), canvasDimension.getHeight())); + new Rectangle2D.Double(0, 0, canvasDimension.getWidth(), canvasDimension.getHeight())); extraSpaceArea.subtract(new Area(extraSpaceRect)); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f)); g2d.setTransform(extraSpaceTx); @@ -510,6 +526,123 @@ public class DrawingManager { return failedComponents; } + private void drawMirroredComponent(Project project, Set drawOptions, + Double scaleFactor, Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, + boolean outlineMode, int i, ComponentState state, double boardMirrorSpacing, + IDIYComponent componentToDraw, IBoard board) { + Rectangle2D boardRectangle = board.getBoardRectangle(); + AffineTransform mirrorTx = null; + switch (board.getBoardUndersideDisplay()) { + case ABOVE: + mirrorTx = new AffineTransform(); + mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMinY()); + mirrorTx.scale(1, -1); + mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMinY()); + mirrorTx.translate(0, boardMirrorSpacing); + break; + case BELOW: + mirrorTx = new AffineTransform(); + mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMaxY()); + mirrorTx.scale(1, -1); + mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMaxY()); + mirrorTx.translate(0, -boardMirrorSpacing); + break; + case LEFT: + mirrorTx = new AffineTransform(); + mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMinY()); + mirrorTx.scale(-1, 1); + mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMinY()); + mirrorTx.translate(boardMirrorSpacing, 0); + break; + case RIGHT: + mirrorTx = new AffineTransform(); + mirrorTx.translate(boardRectangle.getMaxX(), boardRectangle.getMinY()); + mirrorTx.scale(-1, 1); + mirrorTx.translate(-boardRectangle.getMaxX(), -boardRectangle.getMinY()); + mirrorTx.translate(-boardMirrorSpacing, 0); + break; + default: + LOG.warn("Unexpected board underside display: " + board.getBoardUndersideDisplay()); + break; + } + AffineTransform txBefore = new AffineTransform(g2dWrapper.getTransform()); + byte alphaBefore = 0; + if (AbstractTransparentComponent.class.isInstance(componentToDraw)) { + alphaBefore = ((AbstractTransparentComponent)componentToDraw).getAlpha(); + ((AbstractTransparentComponent)componentToDraw).setAlpha(MIRRORED_ALPHA); + } + try { + g2dWrapper.setMirrorTransform(mirrorTx); + g2dWrapper.transform(mirrorTx); + + drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper, outlineMode, i, componentToDraw, state, true); + +// componentToDraw.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); + } finally { + g2dWrapper.setTransform(txBefore); + g2dWrapper.setMirrorTransform(null); + if (AbstractTransparentComponent.class.isInstance(componentToDraw)) { + ((AbstractTransparentComponent)componentToDraw).setAlpha(alphaBefore); + } + } + } + + private void drawComponent(Project project, Set drawOptions, Double scaleFactor, + Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, boolean outlineMode, int i, + IDIYComponent component, ComponentState state, boolean mirrored) { + + AffineTransform txBefore = new AffineTransform(g2dWrapper.getTransform()); + try { + 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, mirrored); + else // go straight to the wrapper + component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); + + if (g2dWrapper.isTrackingContinuityArea()) { + LOG.debug("Component " + component.getName() + " of type " + + component.getClass().getName() + " did not stop tracking continuity area."); + } + } finally { + g2dWrapper.setTransform(txBefore); + } + } + + private void configureG2D(Graphics2D g2d, Set drawOptions) { + if (drawOptions.contains(DrawOption.ANTIALIASING)) { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + } + if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) { + g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + // g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, + // RenderingHints.VALUE_STROKE_PURE); + } else { + g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_SPEED); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); + g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + } + private double zoomCached = 0; private Dimension canvasDimensionCached; private double extraSpaceCached; @@ -517,8 +650,8 @@ public class DrawingManager { private BufferedImage cachedGridImage; private void drawGrid(Project project, Graphics2D g2dIn, double zoom, double extraSpace, - Dimension canvasDimension, Rectangle2D visibleRect, Set drawOptions, - Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) { + Dimension canvasDimension, Rectangle2D visibleRect, Set drawOptions, + Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) { GridType gridType = GridType.LINES; @@ -526,10 +659,10 @@ public class DrawingManager { // draw from cache if (zoom != zoomCached || !canvasDimension.equals(canvasDimensionCached) - || extraSpace != extraSpaceCached || gridType != gridTypeCached) { + || extraSpace != extraSpaceCached || gridType != gridTypeCached) { try { cachedGridImage = new BufferedImage((int) canvasDimension.getWidth(), - (int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB); + (int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB); // create graphics Graphics2D g2d = cachedGridImage.createGraphics(); @@ -538,11 +671,11 @@ public class DrawingManager { float gridThickness = (float) (1f * (zoom > 1 ? 1 : zoom)); if (gridType == GridType.CROSSHAIR) { g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10f, - new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4)); + BasicStroke.JOIN_MITER, 10f, + new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4)); } else if (gridType == GridType.DOT) { g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10f, new float[] {1f, (float) zoomStep - 1}, 0f)); + BasicStroke.JOIN_MITER, 10f, new float[] {1f, (float) zoomStep - 1}, 0f)); } else { g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness)); } @@ -559,9 +692,9 @@ public class DrawingManager { g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness * 3)); g2d.setColor(theme.getDotColor()); for (double i = extraSpace + zoomStep * project.getDotSpacing(); i < extraSpace - + innerCanvasDimension.width; i += zoomStep * project.getDotSpacing()) + + innerCanvasDimension.width; i += zoomStep * project.getDotSpacing()) for (double j = extraSpace + zoomStep * project.getDotSpacing(); j < extraSpace - + innerCanvasDimension.height; j += zoomStep * project.getDotSpacing()) { + + innerCanvasDimension.height; j += zoomStep * project.getDotSpacing()) { g2d.fillOval((int) Math.round(i - 1), (int) Math.round(j - 1), 3, 3); } } @@ -573,12 +706,12 @@ public class DrawingManager { gridTypeCached = gridType; } } - + if (cachedGridImage == null) { LOG.warn("Cached grid image is null!"); return; } - + if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) { g2dIn.drawImage(cachedGridImage, 0, 0, null); } @@ -587,10 +720,10 @@ public class DrawingManager { if (drawOptions.contains(DrawOption.EXTRA_SPACE)) { float borderThickness = (float) (3f * (zoom > 1 ? 1 : zoom)); g2dIn.setStroke(ObjectCache.getInstance().fetchStroke(borderThickness, - new float[] {borderThickness * 4, borderThickness * 4,}, 0, BasicStroke.CAP_BUTT)); + new float[] {borderThickness * 4, borderThickness * 4,}, 0, BasicStroke.CAP_BUTT)); g2dIn.setColor(theme.getOutlineColor()); extraSpaceRect.setRect(new Rectangle2D.Double(extraSpace, extraSpace, - innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight())); + innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight())); g2dIn.draw(extraSpaceRect); extraSpaceTx.setTransform(g2dIn.getTransform()); @@ -598,7 +731,7 @@ public class DrawingManager { g2dIn.transform(AffineTransform.getTranslateInstance(extraSpace, extraSpace)); if (visibleRect != null) visibleRect.setRect(visibleRect.getX() - extraSpace, visibleRect.getY() - extraSpace, - visibleRect.getWidth(), visibleRect.getHeight()); + visibleRect.getWidth(), visibleRect.getHeight()); } } @@ -607,9 +740,9 @@ public class DrawingManager { if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) { lastStatsReportedTime = System.currentTimeMillis(); String mapAsString = renderStatsByType.entrySet().stream() - .sorted( - (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) - .map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}")); + .sorted( + (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) + .map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}")); LOG.debug("Render stats: " + mapAsString); LOG.debug("Page stats: " + totalStats.toAvgString()); DrawingCache.Instance.logStats(); @@ -664,11 +797,11 @@ public class DrawingManager { double height = project.getHeight().convertToPixels(); double targetExtraSpace = EXTRA_SPACE * Math.max(width, height); return CalcUtils.roundToGrid(targetExtraSpace, - project.getGridSpacing().scale(project.getDotSpacing())); + project.getGridSpacing().scale(project.getDotSpacing())); } public Dimension getCanvasDimensions(Project project, Double zoomLevel, - boolean includeExtraSpace) { + boolean includeExtraSpace) { double width = project.getWidth().convertToPixels(); double height = project.getHeight().convertToPixels(); @@ -705,7 +838,7 @@ public class DrawingManager { currentContinuityAreas = continuityGraphCache.findAreasFor(p); } - + public List getContinuityAreas() { // Find all individual continuity areas for all components List preliminaryAreas = new ArrayList(); @@ -716,17 +849,17 @@ public class DrawingManager { if (a == null || a.getOutlineArea() == null) continue; - + IDIYComponent component = entry.getKey(); - + int layerId; - + if (component instanceof ILayeredComponent) { layerId = ((ILayeredComponent)component).getLayerId(); } else { layerId = 0; } - + Collection positiveAreas = a.getContinuityPositiveAreas(); if (positiveAreas != null) { for (Area a1 : positiveAreas) { @@ -734,7 +867,7 @@ public class DrawingManager { checkBreakout.add(false); } } - + Collection negativeAreas = a.getContinuityNegativeAreas(); if (negativeAreas != null) { for (Area na : negativeAreas) { @@ -759,9 +892,9 @@ public class DrawingManager { areas.add(a); } else { areas.addAll(breakoutAreas.stream() - .map(area -> new ContinuityArea(a.getLayerId(), area)) - .collect(Collectors.toList())); - } + .map(area -> new ContinuityArea(a.getLayerId(), area)) + .collect(Collectors.toList())); + } } else areas.add(a); } @@ -781,7 +914,7 @@ public class DrawingManager { } z++; } - + List areas = getContinuityAreas(); return NetlistBuilder.buildContinuityGraph(areas, connections); @@ -790,9 +923,9 @@ public class DrawingManager { public List getContinuityAreaProximity(float threshold) { List continuityAreas = getContinuityAreas(); Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for - // approx good results + // approx good results List expanded = continuityAreas.parallelStream() - .map((a) -> new Area(s.createStrokedShape(a.getArea()))).collect(Collectors.toList()); + .map((a) -> new Area(s.createStrokedShape(a.getArea()))).collect(Collectors.toList()); List intersections = new ArrayList(); for (int i = 0; i < expanded.size() - 1; i++) { for (int j = i + 1; j < expanded.size(); j++) { @@ -812,13 +945,13 @@ public class DrawingManager { Rectangle2D bounds2d = a.getBounds2D(); if (bounds2d.getWidth() < minSize) bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(), - minSize, bounds2d.getHeight()); + minSize, bounds2d.getHeight()); if (bounds2d.getHeight() < minSize) bounds2d = new Rectangle2D.Double(bounds2d.getX(), bounds2d.getCenterY() - minSize / 2, - bounds2d.getWidth(), minSize); + bounds2d.getWidth(), minSize); shapes.add(new Area(new Ellipse2D.Double(bounds2d.getX(), bounds2d.getY(), - bounds2d.getWidth(), bounds2d.getHeight()))); + bounds2d.getWidth(), bounds2d.getHeight()))); } // AreaUtils.crunchAreas(shapes, null); @@ -827,4 +960,4 @@ public class DrawingManager { return shapes; } -} +} \ No newline at end of file diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java index f5fac8a0..766e41f3 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java @@ -1,17 +1,17 @@ /* - * + * * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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 * . */ @@ -62,7 +62,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; - import org.diylc.common.ObjectCache; import org.diylc.common.ZoomableStroke; import org.diylc.core.IDrawingObserver; @@ -70,24 +69,24 @@ 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. - * + * * @author Branislav Stojkovic */ class G2DWrapper extends Graphics2D implements IDrawingObserver { public static int LINE_SENSITIVITY_MARGIN = 2; - public static int CURVE_SENSITIVITY = 6; + public static int CURVE_SENSITIVITY = 6; private boolean drawingComponent = false; private boolean trackingAllowed = true; private boolean trackingContinuityAllowed = false; private boolean trackingContinuityPositive = true; - + private HashMap trackingStacks = new HashMap(); - + private int uniqueCounter = 0; private Graphics2D canvasGraphics; @@ -105,11 +104,13 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { private Shape lastShape; private double zoom; - private int zOrder; + private int zOrder; + + private AffineTransform mirrorTransform; /** * Creates a wrapper around specified {@link Graphics2D} object. - * + * * @param canvasGraphics */ public G2DWrapper(Graphics2D canvasGraphics, double zoom) { @@ -122,14 +123,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { continuityMarker = null; currentTx = new AffineTransform(); } - + public Graphics2D getCanvasGraphics() { return canvasGraphics; } /** * Clears out the current area and caches canvas settings. - * + * * @param zOrder */ public void startedDrawingComponent(int zOrder) { @@ -153,7 +154,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { /** * Reverts {@link Graphics2D} settings and returns area drawn by component in the meantime. - * + * * @return */ public ComponentArea finishedDrawingComponent() { @@ -163,27 +164,27 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { canvasGraphics.setTransform(originalTx); canvasGraphics.setComposite(originalComposite); canvasGraphics.setFont(originalFont); -// Map sorted = sortByComparator(trackingStacks, false); - return new ComponentArea(zOrder,currentArea, new ArrayList(continuityPositiveAreas.values()), - new ArrayList(continuityNegativeAreas.values())); +// Map sorted = sortByComparator(trackingStacks, false); + return new ComponentArea(zOrder,currentArea, new ArrayList(continuityPositiveAreas.values()), + new ArrayList(continuityNegativeAreas.values())); } - + // start - used for caching - + public Area getCurrentArea() { return currentArea; } - + public Map getContinuityPositiveAreas() { return continuityPositiveAreas; } - + public Map getContinuityNegativeAreas() { return continuityNegativeAreas; } - + // end - used for caching - + public void merge(Area currentArea, Map continuityPositiveAreas, Map continuityNegativeAreas) { this.currentArea = currentArea; this.continuityPositiveAreas = continuityPositiveAreas; @@ -212,18 +213,22 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { } @Override - public boolean isTrackingContinuityArea() { + public boolean isTrackingContinuityArea() { return trackingContinuityAllowed; - } - + } + @Override public void setContinuityMarker(String marker) { - continuityMarker = marker; - } + continuityMarker = marker; + } + + public void setMirrorTransform(AffineTransform mirrorTransform) { + this.mirrorTransform = mirrorTransform; + } /** * Appends shape interior to the current component area. - * + * * @param s */ private void appendShape(Shape s) { @@ -237,7 +242,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { area.transform(currentTx); if (trackingAllowed) - currentArea.add(area); + currentArea.add(area); Map toAdd; if (trackingContinuityAllowed) { @@ -250,18 +255,18 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { toAdd.put(marker, area); // markStack(); } else { - Area current = toAdd.getOrDefault(marker, null); - if (current == null) { + Area current = toAdd.getOrDefault(marker, null); + if (current == null) { toAdd.put(marker, area); -// markStack(); +// markStack(); } else current.add(area); - } + } } lastShape = s; } } - + @SuppressWarnings("unused") private void markStack() { StringWriter sw = new StringWriter(); @@ -275,7 +280,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { /** * Appends shape outline to the current component area. - * + * * @param s */ private void appendShapeOutline(Shape s) { @@ -345,22 +350,73 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { @Override public void drawString(String str, int x, int y) { - canvasGraphics.drawString(str, x, y); - if (drawingComponent && trackingAllowed) { - FontMetrics fontMetrics = canvasGraphics.getFontMetrics(); - Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics); - Point2D point = new Point2D.Double(x, y); - // currentTx.transform(point, point); - Rectangle2D finalRec = - new Rectangle2D.Double(rect.getX() + point.getX(), rect.getY() + point.getY(), rect.getWidth(), - rect.getHeight()); - appendShape(finalRec); - } + this.drawString(str, (float)x, (float)y); } @Override public void drawString(String str, float x, float y) { - canvasGraphics.drawString(str, x, y); + if (this.mirrorTransform == null) { + canvasGraphics.drawString(str, x, y); + } else { + drawStringUnmirrored(str, x, y); + } + handleDrawString(str, x, y); + } + + private void drawStringUnmirrored(String str, float x, float y) { + // Save the current transform (T), which includes the mirror. + AffineTransform originalTransform = new AffineTransform(getTransform()); + + // Compute the non-mirrored transform A by "canceling" the mirror: + // Since T = A ? M and M is self-inverse, we have A = T ? M. + AffineTransform nonMirroredTransform = new AffineTransform(originalTransform); + nonMirroredTransform.concatenate(mirrorTransform); + + GlyphVector gv = getFont().createGlyphVector(this.getFontRenderContext(), str); + Rectangle2D visualBounds = gv.getVisualBounds(); + // Use a temporary Graphics2D (with transform A) to obtain correct FontMetrics. + Graphics2D gTemp = (Graphics2D) canvasGraphics.create(); + gTemp.setTransform(nonMirroredTransform); + FontMetrics fm = gTemp.getFontMetrics(); + double textWidth = fm.stringWidth(str); + // Use ascent + descent as the bounding box height. + double textHeight = visualBounds.getHeight();//fm.getAscent() + fm.getDescent(); + double ascent = fm.getAscent(); + gTemp.dispose(); + + // 1. Compute the center of the text?s bounding box in the mirrored (T) space. + Point2D centerMirrored = new Point2D.Double(x + textWidth / 2.0, + y + textHeight / 2.0); + + // 2. Convert that center to screen coordinates using the original transform T. + Point2D centerScreen = originalTransform.transform(centerMirrored, null); + + // 3. Map the screen coordinate into the non-mirrored coordinate system (A). + Point2D centerNonMirrored; + try { + centerNonMirrored = nonMirroredTransform.createInverse().transform(centerScreen, null); + } catch (NoninvertibleTransformException e) { + e.printStackTrace(); + centerNonMirrored = centerMirrored; // Fallback if inversion fails. + } + + // 4. Compute the new top-left in A so that the bounding box (width x height) + // remains centered at centerNonMirrored. + double newTopLeftX = centerNonMirrored.getX() - textWidth / 2.0; + double newTopLeftY = centerNonMirrored.getY() - textHeight / 2.0; + // Adjust for the baseline (drawString uses the baseline for y) + float drawX = (float) newTopLeftX; + float drawY = (float) (newTopLeftY + ascent); + + // 5. Set the transform to the non-mirrored transform (A) so that mirror is canceled. + canvasGraphics.setTransform(nonMirroredTransform); + canvasGraphics.drawString(str, drawX, drawY); + + // 6. Restore the original transform. + setTransform(originalTransform); + } + + private void handleDrawString(String str, float x, float y) { if (drawingComponent && trackingAllowed) { FontMetrics fontMetrics = canvasGraphics.getFontMetrics(); Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics); @@ -569,7 +625,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { canvasGraphics.drawArc(x, y, width, height, startAngle, arcAngle); if (drawingComponent && (trackingAllowed || trackingContinuityAllowed)) { appendShapeOutline(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); - } + } } @Override @@ -599,14 +655,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, - ImageObserver observer) { + ImageObserver observer) { // FIXME: map return canvasGraphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, - Color bgcolor, ImageObserver observer) { + Color bgcolor, ImageObserver observer) { // FIXME: map return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); } @@ -757,12 +813,12 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { public void setXORMode(Color c1) { canvasGraphics.setXORMode(c1); } - + @SuppressWarnings("unused") private static Map sortByComparator(Map unsortMap, - final boolean order) { + final boolean order) { List> list = - new LinkedList>(unsortMap.entrySet()); + new LinkedList>(unsortMap.entrySet()); // Sorting the list based on values Collections.sort(list, new Comparator>() { @@ -784,4 +840,4 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { return sortedMap; } -} +} \ No newline at end of file diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/boards/AbstractBoard.java b/diylc/diylc-library/src/main/java/org/diylc/components/boards/AbstractBoard.java index e64effa2..30915872 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/boards/AbstractBoard.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/boards/AbstractBoard.java @@ -28,14 +28,15 @@ import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; 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.VerticalAlignment; import org.diylc.components.AbstractTransparentComponent; +import org.diylc.core.BoardUndersideDisplay; import org.diylc.core.ComponentState; +import org.diylc.core.IBoard; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; import org.diylc.core.VisibilityPolicy; @@ -43,9 +44,10 @@ import org.diylc.core.annotations.EditableProperty; import org.diylc.core.measures.Size; import org.diylc.core.measures.SizeUnit; -public abstract class AbstractBoard extends AbstractTransparentComponent { +public abstract class AbstractBoard extends AbstractTransparentComponent implements IBoard { private static final long serialVersionUID = 1L; + public static final Size DEFAULT_UNDERSIDE_OFFSET = new Size(0.1, SizeUnit.in); public static Color BORDER_COLOR = PHENOLIC_COLOR.darker(); public static Color COORDINATE_COLOR = Color.gray.brighter(); @@ -56,7 +58,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent protected String value = ""; // two control points used for controlling the position protected Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0), - new Point2D.Double(DEFAULT_WIDTH.convertToPixels(), DEFAULT_HEIGHT.convertToPixels())}; + new Point2D.Double(DEFAULT_WIDTH.convertToPixels(), DEFAULT_HEIGHT.convertToPixels())}; // top-left and bottom-right points protected Point2D.Double firstPoint = new Point2D.Double(); protected Point2D.Double secondPoint = new Point2D.Double(); @@ -69,17 +71,20 @@ public abstract class AbstractBoard extends AbstractTransparentComponent protected CoordinateOrigin coordinateOrigin = CoordinateOrigin.Top_Left; protected CoordinateDisplay coordinateDisplay = CoordinateDisplay.One_Side; protected CoordinateType yType = CoordinateType.Letters; - + protected Size length; protected Size width; protected BoardSizingMode mode = BoardSizingMode.TwoPoints; + protected BoardUndersideDisplay boardUndersideDisplay = BoardUndersideDisplay.NONE; + protected Size undersideOffset = DEFAULT_UNDERSIDE_OFFSET; + @Override public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, - IDrawingObserver drawingObserver) { + IDrawingObserver drawingObserver) { g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); - - Point2D finalSecondPoint = getFinalSecondPoint(); + + Point2D finalSecondPoint = getFinalSecondPoint(); Composite oldComposite = g2d.getComposite(); // render as transparent when dragging @@ -90,64 +95,64 @@ public abstract class AbstractBoard extends AbstractTransparentComponent g2d.setColor(boardColor); g2d.fillRect((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(); g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR - : borderColor); + : borderColor); g2d.drawRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY())); } - + protected Point2D getFinalSecondPoint() { Point2D finalSecondPoint; if (getMode() == BoardSizingMode.TwoPoints) finalSecondPoint = secondPoint; else - finalSecondPoint = new Point2D.Double(firstPoint.getX() + getLength().convertToPixels(), - firstPoint.getY() + getWidth().convertToPixels()); + finalSecondPoint = new Point2D.Double(firstPoint.getX() + getLength().convertToPixels(), + firstPoint.getY() + getWidth().convertToPixels()); return finalSecondPoint; } protected void drawCoordinates(Graphics2D g2d, double spacing, Project project) { g2d.setColor(coordinateColor); g2d.setFont(project.getFont().deriveFont(COORDINATE_FONT_SIZE)); - + // The half space is used to do rounding when calculating the range. double halfSpace = spacing / 2; CoordinateOrigin origin = getCoordinateOrigin(); - + Point2D finalSecondPoint = getFinalSecondPoint(); - + if (getCoordinateDisplay() != CoordinateDisplay.None) { int range; double yOffset; CoordinateType yType = getyType(); double y = firstPoint.getY(); - + if (origin == CoordinateOrigin.Top_Left || origin == CoordinateOrigin.Top_Right) { - range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing); - yOffset = spacing; + range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing); + yOffset = spacing; } else { - range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing); - yOffset = -spacing; - y = finalSecondPoint.getY(); + range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing); + yOffset = -spacing; + y = finalSecondPoint.getY(); } - + for (int c = 1; c < range; c++) { - int xOffset = (yType == CoordinateType.Numbers && c >= 10) || (yType == CoordinateType.Letters && c >= 27) ? 0 : 2; - String label = yType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c); - - y += yOffset; - - StringUtils.drawCenteredText(g2d, label, - firstPoint.getX() + xOffset, y, - HorizontalAlignment.LEFT, VerticalAlignment.CENTER); - if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) { - StringUtils.drawCenteredText(g2d, label, - finalSecondPoint.getX() - xOffset, y, - HorizontalAlignment.RIGHT, VerticalAlignment.CENTER); - } + int xOffset = (yType == CoordinateType.Numbers && c >= 10) || (yType == CoordinateType.Letters && c >= 27) ? 0 : 2; + String label = yType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c); + + y += yOffset; + + StringUtils.drawCenteredText(g2d, label, + firstPoint.getX() + xOffset, y, + HorizontalAlignment.LEFT, VerticalAlignment.CENTER); + if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) { + StringUtils.drawCenteredText(g2d, label, + finalSecondPoint.getX() - xOffset, y, + HorizontalAlignment.RIGHT, VerticalAlignment.CENTER); + } } } @@ -155,8 +160,8 @@ public abstract class AbstractBoard extends AbstractTransparentComponent int range; double xOffset; CoordinateType xType = getxType(); - double x = firstPoint.getX(); - + double x = firstPoint.getX(); + if (origin == CoordinateOrigin.Top_Left || origin == CoordinateOrigin.Bottom_Left) { range = (int) ((finalSecondPoint.getX() - firstPoint.getX() + halfSpace) / spacing); xOffset = spacing; @@ -165,19 +170,19 @@ public abstract class AbstractBoard extends AbstractTransparentComponent xOffset = -spacing; x = finalSecondPoint.getX(); } - + for (int c = 1; c < range; c++) { String label = xType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c); - + x += xOffset; - - StringUtils.drawCenteredText(g2d, label, - x, firstPoint.getY() + 2, - HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + + StringUtils.drawCenteredText(g2d, label, + x, firstPoint.getY() + 2, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) { - StringUtils.drawCenteredText(g2d, label, - x, (int) (finalSecondPoint.getY() - COORDINATE_FONT_SIZE), - HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, label, + x, (int) (finalSecondPoint.getY() - COORDINATE_FONT_SIZE), + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); } } } @@ -225,7 +230,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent public void setBorderColor(Color borderColor) { this.borderColor = borderColor; } - + @EditableProperty(name = "X") public CoordinateType getxType() { if (xType == null) @@ -250,15 +255,15 @@ public abstract class AbstractBoard extends AbstractTransparentComponent @EditableProperty(name = "Coordinate Origin") public CoordinateOrigin getCoordinateOrigin() { - if (coordinateOrigin == null) - coordinateOrigin = CoordinateOrigin.Top_Left; - return coordinateOrigin; + if (coordinateOrigin == null) + coordinateOrigin = CoordinateOrigin.Top_Left; + return coordinateOrigin; } - + public void setCoordinateOrigin(CoordinateOrigin coordinateOrigin) { - this.coordinateOrigin = coordinateOrigin; + this.coordinateOrigin = coordinateOrigin; } - + @EditableProperty(name = "Y") public CoordinateType getyType() { if (yType == null) @@ -294,9 +299,9 @@ public abstract class AbstractBoard extends AbstractTransparentComponent public void setControlPoint(Point2D point, int index) { controlPoints[index].setLocation(point); firstPoint.setLocation(Math.min(controlPoints[0].getX(), controlPoints[1].getX()), - Math.min(controlPoints[0].getY(), controlPoints[1].getY())); + Math.min(controlPoints[0].getY(), controlPoints[1].getY())); secondPoint.setLocation(Math.max(controlPoints[0].getX(), controlPoints[1].getX()), - Math.max(controlPoints[0].getY(), controlPoints[1].getY())); + Math.max(controlPoints[0].getY(), controlPoints[1].getY())); } @EditableProperty @@ -309,13 +314,13 @@ public abstract class AbstractBoard extends AbstractTransparentComponent public void setValue(String value) { this.value = value; } - + private SizeUnit getDefaultUnit() { if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.METRIC_KEY, true)) return SizeUnit.mm; - return SizeUnit.in; + return SizeUnit.in; } - + @EditableProperty(name = "Explicit Length", validatorClass = BoardModeValidator.class) public Size getLength() { if (getMode() == BoardSizingMode.TwoPoints) { @@ -327,12 +332,12 @@ public abstract class AbstractBoard extends AbstractTransparentComponent public void setLength(Size length) { if (getMode() == BoardSizingMode.Explicit && length != null && width != null) { - setControlPoint(firstPoint, 0); + setControlPoint(firstPoint, 0); Point2D second = new Point2D.Double(firstPoint.getX() + length.convertToPixels(), - firstPoint.getY() + width.convertToPixels()); - setControlPoint(second, 1); + firstPoint.getY() + width.convertToPixels()); + setControlPoint(second, 1); } - this.length = length; + this.length = length; } @EditableProperty(name = "Explicit Width", validatorClass = BoardModeValidator.class) @@ -352,7 +357,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent } this.width = width; } - + @EditableProperty(name = "Dimension Mode") public BoardSizingMode getMode() { if (mode == null) @@ -363,27 +368,66 @@ public abstract class AbstractBoard extends AbstractTransparentComponent public void setMode(BoardSizingMode mode) { this.mode = mode; } - + + @EditableProperty(name = "Underside Display") + @Override + public BoardUndersideDisplay getBoardUndersideDisplay() { + if (this.boardUndersideDisplay == null) { + this.boardUndersideDisplay = BoardUndersideDisplay.NONE; + } + return this.boardUndersideDisplay; + } + + public void setBoardUndersideDisplay(BoardUndersideDisplay boardUndersideDisplay) { + this.boardUndersideDisplay = boardUndersideDisplay; + } + + @EditableProperty(name = "Underside Offset") + @Override + public Size getUndersideOffset() { + if (undersideOffset == null) { + undersideOffset = DEFAULT_UNDERSIDE_OFFSET; + } + return undersideOffset; + } + + public void setUndersideOffset(Size undersideOffset) { + this.undersideOffset = undersideOffset; + } + + @Override + public Rectangle2D getBoardRectangle() { + Point2D finalSecondPoint = getFinalSecondPoint(); + + return new Rectangle2D.Double(firstPoint.getX(), firstPoint.getY(), + finalSecondPoint.getX() - firstPoint.getX(), finalSecondPoint.getY() - firstPoint.getY()); + } + + @Override + public boolean shouldExportToGerber() { + return false; + } + @Override public Rectangle2D getCachingBounds() { - Point2D finalSecondPoint = getFinalSecondPoint(); - return new Rectangle2D.Double(Math.min(firstPoint.getX(), finalSecondPoint.getX()) - 2, Math.min(firstPoint.getY(), finalSecondPoint.getY()) - 2, - Math.abs(finalSecondPoint.getX() - firstPoint.getX()) + 4, Math.abs(finalSecondPoint.getY() - firstPoint.getY()) + 4); + Point2D finalSecondPoint = getFinalSecondPoint(); + return new Rectangle2D.Double(Math.min(firstPoint.getX(), finalSecondPoint.getX()) - 2, Math.min(firstPoint.getY(), finalSecondPoint.getY()) - 2, + Math.abs(finalSecondPoint.getX() - firstPoint.getX()) + 4, Math.abs(finalSecondPoint.getY() - firstPoint.getY()) + 4); } public static enum CoordinateType { Letters, Numbers } - + public static enum CoordinateDisplay { None, One_Side, Both_Sides; - + @Override public String toString() { return super.toString().replace('_', ' '); }; } - + public static enum CoordinateOrigin { Top_Left, Top_Right, Bottom_Right, Bottom_Left; @@ -392,19 +436,19 @@ public abstract class AbstractBoard extends AbstractTransparentComponent return super.toString().replace('_', ' '); }; } - + public static enum BoardSizingMode { TwoPoints("Opposing Points"), Explicit("Explicit Dimensions"); - + private String label; private BoardSizingMode(String label) { this.label = label; } - + @Override public String toString() { return label; } - } -} + } +} \ No newline at end of file diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/boards/BlankBoard.java b/diylc/diylc-library/src/main/java/org/diylc/components/boards/BlankBoard.java index 5fda6b2c..eb697516 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/boards/BlankBoard.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/boards/BlankBoard.java @@ -28,7 +28,6 @@ import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; - import org.diylc.common.ObjectCache; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.ComponentState; @@ -38,12 +37,11 @@ import org.diylc.core.Project; import org.diylc.core.annotations.BomPolicy; import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.EditableProperty; -import org.diylc.core.gerber.IGerberBoard; @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) -public class BlankBoard extends AbstractBoard implements IGerberBoard { + zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board", + bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, transformer = SimpleComponentTransformer.class) +public class BlankBoard extends AbstractBoard { private static final long serialVersionUID = 1L; @@ -60,14 +58,14 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard { @Override public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, - IDrawingObserver drawingObserver) { - Point2D finalSecondPoint = getFinalSecondPoint(); - + IDrawingObserver drawingObserver) { + Point2D finalSecondPoint = getFinalSecondPoint(); + Shape clip = g2d.getClip(); if (checkPointsClipped(clip) && !clip.contains(firstPoint.getX(), finalSecondPoint.getY()) - && !clip.contains(finalSecondPoint.getX(), firstPoint.getY())) { + && !clip.contains(finalSecondPoint.getX(), firstPoint.getY())) { return; - } + } g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); if (componentState != ComponentState.DRAGGING) { @@ -86,13 +84,13 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard { // tracked so far. drawingObserver.stopTracking(); g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR - : borderColor); + : borderColor); if (getType() == Type.SQUARE) g2d.drawRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY())); else g2d.drawOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY())); } - + @Override public CoordinateType getxType() { // Override to prevent editing. @@ -128,12 +126,17 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard { public void setType(Type type) { this.type = type; } - + @Override public String getControlPointNodeName(int index) { return null; } + @Override + public boolean shouldExportToGerber() { + return true; + } + static enum Type { ROUND, SQUARE; @@ -142,12 +145,4 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard { return name().substring(0, 1) + name().substring(1).toLowerCase(); } } - - @Override - public Rectangle2D getBoardRectangle() { - Point2D finalSecondPoint = getFinalSecondPoint(); - - return new Rectangle2D.Double(firstPoint.getX(), firstPoint.getY(), - finalSecondPoint.getX() - firstPoint.getX(), finalSecondPoint.getY() - firstPoint.getY()); - } -} +} \ No newline at end of file diff --git a/diylc/diylc-swing/dict.txt b/diylc/diylc-swing/dict.txt index 65525c4c..08cc1da9 100644 --- a/diylc/diylc-swing/dict.txt +++ b/diylc/diylc-swing/dict.txt @@ -583,6 +583,8 @@ Tubular Beveled Two Type Ultra-miniature +Underside Display +Underside Offset Underside Pad Color Underside Trace Color Undo From 5c745d2d45b50f2dfbb188762857185159362e43 Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Fri, 14 Mar 2025 14:06:14 +0100 Subject: [PATCH 02/32] mirror --- .../org/diylc/presenter/DrawingCache.java | 24 +- .../org/diylc/presenter/DrawingManager.java | 349 ++++++++---------- .../java/org/diylc/presenter/G2DWrapper.java | 170 +++------ .../transform/CliffJackTransformer.java | 2 +- diylc/diylc-swing/dict.txt | 3 + 5 files changed, 219 insertions(+), 329 deletions(-) diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java index 798c7d7b..e0aacfe2 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingCache.java @@ -74,7 +74,7 @@ public class DrawingCache { .extractComponentTypeFrom((Class>) x.getComponent().getClass()); if (type != null && type.getEnableCache()) { - renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder(), false); + renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder()); } }); } @@ -89,14 +89,10 @@ public class DrawingCache { * @param outlineMode * @param project * @param zoom - * @param scaleFactor - * @param zOrder - * @param mirrored */ @SuppressWarnings("unchecked") public void draw(IDIYComponent component, G2DWrapper g2d, ComponentState componentState, - boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Rectangle2D visibleRect, - boolean mirrored) { + boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Rectangle2D visibleRect) { ComponentType type = ComponentProcessor.getInstance() .extractComponentTypeFrom((Class>) component.getClass()); @@ -104,7 +100,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, mirrored); + CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, scaleFactor, zOrder); if (value == null) { return; @@ -184,7 +180,7 @@ public class DrawingCache { } private CacheValue renderAndCache(IDIYComponent component, G2DWrapper g2d, ComponentState componentState, - boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, boolean mirrored) { + boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder) { // if we need to apply caching Point2D firstPoint = component.getControlPoint(0); @@ -200,7 +196,7 @@ public class DrawingCache { // only honor the cache if the component hasn't changed in the meantime if (value == null || !value.getComponent().equalsTo(component) || value.getState() != componentState || value.getZoom() != zoom || value.getOutlineMode() != outlineMode - || value.getScaleFactor() != scaleFactor || value.isMirrored() != mirrored) { + || value.getScaleFactor() != scaleFactor) { // figure out size and placement of buffer image Rectangle2D rect = component.getCachingBounds(); int width = (int)Math.round(rect.getWidth() * zoom); @@ -274,7 +270,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, mirrored); + wrapper.getContinuityNegativeAreas(), componentState, outlineMode, zoom, scaleFactor, dx, dy, rect); imageCache.put(component, new SoftReference(value)); } @@ -291,7 +287,6 @@ public class DrawingCache { boolean outlineMode; double zoom; double scaleFactor; - boolean mirrored; int dx; int dy; Rectangle2D cacheBounds; @@ -299,7 +294,7 @@ public class DrawingCache { public CacheValue(IDIYComponent component, BufferedImage image, Area currentArea, Map continuityPositiveAreas, Map continuityNegativeAreas, ComponentState state, boolean outlineMode, double zoom, double scaleFactor, int dx, int dy, - Rectangle2D cacheBounds, boolean mirrored) { + Rectangle2D cacheBounds) { super(); this.cacheBounds = cacheBounds; // take a copy of the component, so we can check if it changed in the meantime @@ -314,7 +309,6 @@ public class DrawingCache { this.state = state; this.outlineMode = outlineMode; this.zoom = zoom; - this.scaleFactor = scaleFactor; this.dx = dx; this.dy = dy; } @@ -355,10 +349,6 @@ public class DrawingCache { return scaleFactor; } - public boolean isMirrored() { - return mirrored; - } - public int getDx() { return dx; } diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java index d14078e4..c272692e 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java @@ -1,17 +1,17 @@ /* - * + * * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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 * . */ @@ -46,26 +46,12 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.log4j.Logger; -import org.checkerframework.checker.units.qual.A; import org.diylc.appframework.miscutils.IConfigurationManager; import org.diylc.appframework.simplemq.MessageDispatcher; -import org.diylc.common.DrawOption; -import org.diylc.common.EventType; -import org.diylc.common.GridType; -import org.diylc.common.IComponentFilter; -import org.diylc.common.IPlugInPort; -import org.diylc.common.ObjectCache; + +import org.diylc.common.*; import org.diylc.components.AbstractTransparentComponent; -import org.diylc.core.BoardUndersideDisplay; -import org.diylc.core.ComponentForRender; -import org.diylc.core.ComponentState; -import org.diylc.core.IBoard; -import org.diylc.core.IContinuity; -import org.diylc.core.IDIYComponent; -import org.diylc.core.ILayeredComponent; -import org.diylc.core.Project; -import org.diylc.core.Theme; -import org.diylc.core.VisibilityPolicy; +import org.diylc.core.*; import org.diylc.core.measures.Size; import org.diylc.core.measures.SizeUnit; import org.diylc.netlist.ContinuityGraph; @@ -75,12 +61,13 @@ import org.diylc.utils.Constants; /** * Utility that deals with painting {@link Project} on the {@link Graphics2D} and keeps areas taken * by each drawn component. - * + * * @author Branislav Stojkovic */ public class DrawingManager { private static final Logger LOG = Logger.getLogger(DrawingManager.class); + public static final byte MIRROR_ALPHA = 64; public static int CONTROL_POINT_SIZE = 7; public static double EXTRA_SPACE = 0.25; @@ -95,8 +82,6 @@ public class DrawingManager { public static Color CONTROL_POINT_COLOR = Color.blue; public static Color SELECTED_CONTROL_POINT_COLOR = Color.green; - private static final Byte MIRRORED_ALPHA = 64; - private Theme theme; // Maps keyed by object reference. @@ -132,7 +117,7 @@ public class DrawingManager { private List proximityMarkers = null; public DrawingManager(MessageDispatcher messageDispatcher, - IConfigurationManager configManager) { + IConfigurationManager configManager) { super(); this.messageDispatcher = messageDispatcher; this.configManager = configManager; @@ -150,16 +135,16 @@ public class DrawingManager { lastDrawnStateMap = new HashMap, ComponentState>(); String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS); debugComponentAreas = - debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true"); + debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true"); String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS); debugContinuityAreas = - debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); + debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); } /** * Paints the project onto the canvas and returns the list of components that failed to draw. - * + * * @param g2d * @param project * @param drawOptions @@ -172,18 +157,16 @@ public class DrawingManager { * @param componentSlot * @param dragInProgress * @param externalZoom - * @param scaleFactor + * @param scaleFactor * @param visibleRect * @return */ - @SuppressWarnings("unlikely-arg-type") public List> drawProject(Graphics2D g2d, Project project, - Set drawOptions, IComponentFilter filter, Rectangle selectionRect, - Collection> selectedComponents, Set> lockedComponents, - Set> groupedComponents, List controlPointSlot, - List> componentSlot, boolean dragInProgress, Double externalZoom, - Double scaleFactor, Rectangle2D visibleRect) { - + Set drawOptions, IComponentFilter filter, Rectangle selectionRect, + Collection> selectedComponents, Set> lockedComponents, + Set> groupedComponents, List controlPointSlot, + List> componentSlot, boolean dragInProgress, Double externalZoom, + Double scaleFactor, Rectangle2D visibleRect) { long totalStartTime = System.nanoTime(); failedComponents.clear(); if (project == null) { @@ -202,7 +185,7 @@ public class DrawingManager { G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom); - configureG2D(g2d, drawOptions); + configureRenderingHints(g2d, drawOptions); // group components with boards that contain them Map>> boardMap = project.getComponents().stream() @@ -210,7 +193,7 @@ public class DrawingManager { .map(IBoard.class::cast) .filter(b -> b.getBoardUndersideDisplay() != BoardUndersideDisplay.NONE) .collect(Collectors.toMap(x -> x, x -> new ArrayList>())); - Map, List> componentBoardMap = new HashMap, List>(); + Map, Set> componentBoardMap = new HashMap, Set>(); for (IBoard board : boardMap.keySet()) { Rectangle2D boardRect = board.getBoardRectangle(); @@ -227,14 +210,14 @@ public class DrawingManager { } if (include) { boardMap.get(board).add(c); - componentBoardMap.computeIfAbsent(c, key -> new ArrayList()).add(board); + componentBoardMap.computeIfAbsent(c, key -> new HashSet()).add(board); } } } // AffineTransform initialTx = g2d.getTransform(); Dimension canvasDimension = - getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE)); + getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE)); g2dWrapper.setColor(theme.getBgColor()); g2dWrapper.fillRect(0, 0, canvasDimension.width, canvasDimension.height); @@ -247,14 +230,14 @@ public class DrawingManager { double extraSpace = getExtraSpace(project) * zoom; drawGrid(project, g2d, zoom, extraSpace, canvasDimension, visibleRect, drawOptions, - extraSpaceRect, extraSpaceTx); + extraSpaceRect, extraSpaceTx); // apply zoom if (Math.abs(1.0 - zoom) > 1e-4) { g2dWrapper.scale(zoom, zoom); if (visibleRect != null) visibleRect.setRect(visibleRect.getX() / zoom, visibleRect.getY() / zoom, - visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); + visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); } // Composite mainComposite = g2d.getComposite(); @@ -267,7 +250,7 @@ public class DrawingManager { Map, ComponentState> componentStateMap = new HashMap, ComponentState>(); List componentForRenderList = new ArrayList(); for (int i = 0; i < project.getComponents().size(); i++) { - IDIYComponent component = project.getComponents().get(i); + IDIYComponent component = project.getComponents().get(i); ComponentState state = ComponentState.NORMAL; if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) { if (dragInProgress) { @@ -291,12 +274,11 @@ public class DrawingManager { for (int i = 0; i < project.getComponents().size(); i++) { IDIYComponent component = project.getComponents().get(i); - + // Do not draw the component if it's filtered out. if (filter != null && !filter.testComponent(component)) { continue; } - ComponentState state = componentStateMap.get(component); // Do not track the area if component is not invalidated and was // drawn in the same state. @@ -317,26 +299,11 @@ public class DrawingManager { long componentStart = System.nanoTime(); try { - AffineTransform txBefore = new AffineTransform(g2d.getTransform()); - drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper, - outlineMode, i, component, state, false); - g2dWrapper.setTransform(txBefore); + drawComponent(project, drawOptions, scaleFactor, visibleRect, component, g2dWrapper, state, zoom, i, outlineMode); - // find one or more boards that contain the component and mirror the component to all - List boards = componentBoardMap.get(component); - if (boards != null) { - for (IBoard board : boards) { - double boardMirrorSpacing = board.getUndersideOffset().convertToPixels(); - drawMirroredComponent(project, drawOptions, scaleFactor, visibleRect, zoom, - g2dWrapper, outlineMode, i, state, boardMirrorSpacing, component, board); - } - } - // if the component is the board itself, mirror it - if (boardMap.keySet().contains(component)) { - IBoard board = (IBoard) component; - double boardMirrorSpacing = board.getUndersideOffset().convertToPixels(); - drawMirroredComponent(project, drawOptions, scaleFactor, visibleRect, zoom, - g2dWrapper, outlineMode, i, state, boardMirrorSpacing, component, (IBoard)component); + if (g2dWrapper.isTrackingContinuityArea()) { + LOG.info("Component " + component.getName() + " of type " + + component.getClass().getName() + " did not stop tracking continuity area."); } } catch (Exception e) { LOG.error("Error drawing " + component.getName(), e); @@ -365,6 +332,29 @@ public class DrawingManager { componentAreaMap.put(component, area); lastDrawnStateMap.put(component, state); } + + // Mirror components that belong to boards that need to be mirrored + if (componentBoardMap.containsKey(component)) { + @SuppressWarnings("unchecked") + ComponentType componentType = ComponentProcessor.getInstance() + .extractComponentTypeFrom((Class>) component.getClass()); + if (componentType.getTransformer() != null) { + Set boards = componentBoardMap.get(component); + for (IBoard board : boards) { + drawMirroredComponent(project, board, component, componentType, g2dWrapper, state, outlineMode); + } + } + } + + // if a component itself is a board that needs to be mirrored, do it + if (IBoard.class.isInstance(component) && componentBoardMap.values().stream() + .anyMatch(boards -> boards.contains(component))) { + ComponentType componentType = ComponentProcessor.getInstance() + .extractComponentTypeFrom((Class>) component.getClass()); + if (componentType.getTransformer() != null) { + drawMirroredComponent(project, (IBoard) component, component, componentType, g2dWrapper, state, outlineMode); + } + } } } @@ -378,17 +368,17 @@ public class DrawingManager { continue; VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i); if ((groupedComponents.contains(component) - && (visibilityPolicy == VisibilityPolicy.ALWAYS + && (visibilityPolicy == VisibilityPolicy.ALWAYS || (selectedComponents.contains(component) - && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) - || (!groupedComponents.contains(component) + && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) + || (!groupedComponents.contains(component) && !selectedComponents.contains(component) && component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) { g2dWrapper.setColor(CONTROL_POINT_COLOR); Point2D controlPoint = component.getControlPoint(i); int pointSize = CONTROL_POINT_SIZE - 2; g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2), - (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); + (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); } } } @@ -397,7 +387,7 @@ public class DrawingManager { for (IDIYComponent component : selectedComponents) { for (int i = 0; i < component.getControlPointCount(); i++) { if (!groupedComponents.contains(component) - && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED + && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED || component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) { Point2D controlPoint = component.getControlPoint(i); @@ -405,11 +395,11 @@ public class DrawingManager { g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2), - (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); + (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1), - (int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, - CONTROL_POINT_SIZE - 2); + (int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, + CONTROL_POINT_SIZE - 2); } } } @@ -423,7 +413,7 @@ public class DrawingManager { try { component.draw(g2dWrapper, ComponentState.NORMAL, - drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); + drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); } catch (Exception e) { LOG.error("Error drawing " + component.getName(), e); @@ -439,12 +429,12 @@ public class DrawingManager { if (point != null) { g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2), - (int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE, - CONTROL_POINT_SIZE); + (int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE, + CONTROL_POINT_SIZE); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1), - (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, - CONTROL_POINT_SIZE - 2); + (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, + CONTROL_POINT_SIZE - 2); } } } @@ -509,7 +499,7 @@ public class DrawingManager { // shade extra space if (drawOptions.contains(DrawOption.EXTRA_SPACE) && SHADE_EXTRA_SPACE && extraSpaceRect != null) { Area extraSpaceArea = new Area( - new Rectangle2D.Double(0, 0, canvasDimension.getWidth(), canvasDimension.getHeight())); + new Rectangle2D.Double(0, 0, canvasDimension.getWidth(), canvasDimension.getHeight())); extraSpaceArea.subtract(new Area(extraSpaceRect)); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f)); g2d.setTransform(extraSpaceTx); @@ -526,121 +516,84 @@ public class DrawingManager { return failedComponents; } - private void drawMirroredComponent(Project project, Set drawOptions, - Double scaleFactor, Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, - boolean outlineMode, int i, ComponentState state, double boardMirrorSpacing, - IDIYComponent componentToDraw, IBoard board) { + private static void drawMirroredComponent(Project project, IBoard board, IDIYComponent component, + ComponentType componentType, G2DWrapper g2dWrapper, ComponentState state, boolean outlineMode) { + double offset = board.getUndersideOffset().convertToPixels(); Rectangle2D boardRectangle = board.getBoardRectangle(); - AffineTransform mirrorTx = null; - switch (board.getBoardUndersideDisplay()) { - case ABOVE: - mirrorTx = new AffineTransform(); - mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMinY()); - mirrorTx.scale(1, -1); - mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMinY()); - mirrorTx.translate(0, boardMirrorSpacing); - break; - case BELOW: - mirrorTx = new AffineTransform(); - mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMaxY()); - mirrorTx.scale(1, -1); - mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMaxY()); - mirrorTx.translate(0, -boardMirrorSpacing); - break; - case LEFT: - mirrorTx = new AffineTransform(); - mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMinY()); - mirrorTx.scale(-1, 1); - mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMinY()); - mirrorTx.translate(boardMirrorSpacing, 0); - break; - case RIGHT: - mirrorTx = new AffineTransform(); - mirrorTx.translate(boardRectangle.getMaxX(), boardRectangle.getMinY()); - mirrorTx.scale(-1, 1); - mirrorTx.translate(-boardRectangle.getMaxX(), -boardRectangle.getMinY()); - mirrorTx.translate(-boardMirrorSpacing, 0); - break; - default: - LOG.warn("Unexpected board underside display: " + board.getBoardUndersideDisplay()); - break; - } - AffineTransform txBefore = new AffineTransform(g2dWrapper.getTransform()); - byte alphaBefore = 0; - if (AbstractTransparentComponent.class.isInstance(componentToDraw)) { - alphaBefore = ((AbstractTransparentComponent)componentToDraw).getAlpha(); - ((AbstractTransparentComponent)componentToDraw).setAlpha(MIRRORED_ALPHA); - } try { - g2dWrapper.setMirrorTransform(mirrorTx); - g2dWrapper.transform(mirrorTx); - - drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper, outlineMode, i, componentToDraw, state, true); - -// componentToDraw.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); - } finally { - g2dWrapper.setTransform(txBefore); - g2dWrapper.setMirrorTransform(null); - if (AbstractTransparentComponent.class.isInstance(componentToDraw)) { - ((AbstractTransparentComponent)componentToDraw).setAlpha(alphaBefore); + IDIYComponent clonedComponent = component.clone(); + int direction = 0; + Point2D pivotPoint = new Point2D.Double(); + switch (board.getBoardUndersideDisplay()) { + case ABOVE: + direction = IComponentTransformer.VERTICAL; + pivotPoint.setLocation(boardRectangle.getMinX(), boardRectangle.getMinY() - offset / 2); + break; + case BELOW: + direction = IComponentTransformer.VERTICAL; + pivotPoint.setLocation(boardRectangle.getMinX(), boardRectangle.getMaxY() + offset / 2); + break; + case LEFT: + direction = IComponentTransformer.HORIZONTAL; + pivotPoint.setLocation(boardRectangle.getMinX() - offset / 2, boardRectangle.getMinY()); + break; + case RIGHT: + direction = IComponentTransformer.HORIZONTAL; + pivotPoint.setLocation(boardRectangle.getMaxX() + offset / 2, boardRectangle.getMinY()); + break; + } + componentType.getTransformer().mirror(clonedComponent, pivotPoint, direction); + if (AbstractTransparentComponent.class.isInstance(clonedComponent)) { + AbstractTransparentComponent transparentComponent = (AbstractTransparentComponent) clonedComponent; + transparentComponent.setAlpha(MIRROR_ALPHA); } + clonedComponent.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); } } - private void drawComponent(Project project, Set drawOptions, Double scaleFactor, - Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, boolean outlineMode, int i, - IDIYComponent component, ComponentState state, boolean mirrored) { - - AffineTransform txBefore = new AffineTransform(g2dWrapper.getTransform()); - try { - 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, mirrored); - else // go straight to the wrapper - component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); - - if (g2dWrapper.isTrackingContinuityArea()) { - LOG.debug("Component " + component.getName() + " of type " - + component.getClass().getName() + " did not stop tracking continuity area."); - } - } finally { - g2dWrapper.setTransform(txBefore); - } + private static void drawComponent(Project project, Set 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); + else // go straight to the wrapper + component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); } - private void configureG2D(Graphics2D g2d, Set drawOptions) { + private void configureRenderingHints(Graphics2D g2d, Set drawOptions) { if (drawOptions.contains(DrawOption.ANTIALIASING)) { g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } else { g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); } if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) { g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, - RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); + RenderingHints.VALUE_COLOR_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); + RenderingHints.VALUE_INTERPOLATION_BILINEAR); // g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, // RenderingHints.VALUE_STROKE_PURE); } else { g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, - RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_SPEED); + RenderingHints.VALUE_COLOR_RENDER_SPEED); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); } g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + RenderingHints.VALUE_FRACTIONALMETRICS_OFF); } private double zoomCached = 0; @@ -650,8 +603,8 @@ public class DrawingManager { private BufferedImage cachedGridImage; private void drawGrid(Project project, Graphics2D g2dIn, double zoom, double extraSpace, - Dimension canvasDimension, Rectangle2D visibleRect, Set drawOptions, - Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) { + Dimension canvasDimension, Rectangle2D visibleRect, Set drawOptions, + Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) { GridType gridType = GridType.LINES; @@ -659,10 +612,10 @@ public class DrawingManager { // draw from cache if (zoom != zoomCached || !canvasDimension.equals(canvasDimensionCached) - || extraSpace != extraSpaceCached || gridType != gridTypeCached) { + || extraSpace != extraSpaceCached || gridType != gridTypeCached) { try { cachedGridImage = new BufferedImage((int) canvasDimension.getWidth(), - (int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB); + (int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB); // create graphics Graphics2D g2d = cachedGridImage.createGraphics(); @@ -671,11 +624,11 @@ public class DrawingManager { float gridThickness = (float) (1f * (zoom > 1 ? 1 : zoom)); if (gridType == GridType.CROSSHAIR) { g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10f, - new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4)); + BasicStroke.JOIN_MITER, 10f, + new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4)); } else if (gridType == GridType.DOT) { g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 10f, new float[] {1f, (float) zoomStep - 1}, 0f)); + BasicStroke.JOIN_MITER, 10f, new float[] {1f, (float) zoomStep - 1}, 0f)); } else { g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness)); } @@ -692,9 +645,9 @@ public class DrawingManager { g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness * 3)); g2d.setColor(theme.getDotColor()); for (double i = extraSpace + zoomStep * project.getDotSpacing(); i < extraSpace - + innerCanvasDimension.width; i += zoomStep * project.getDotSpacing()) + + innerCanvasDimension.width; i += zoomStep * project.getDotSpacing()) for (double j = extraSpace + zoomStep * project.getDotSpacing(); j < extraSpace - + innerCanvasDimension.height; j += zoomStep * project.getDotSpacing()) { + + innerCanvasDimension.height; j += zoomStep * project.getDotSpacing()) { g2d.fillOval((int) Math.round(i - 1), (int) Math.round(j - 1), 3, 3); } } @@ -706,12 +659,12 @@ public class DrawingManager { gridTypeCached = gridType; } } - + if (cachedGridImage == null) { LOG.warn("Cached grid image is null!"); return; } - + if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) { g2dIn.drawImage(cachedGridImage, 0, 0, null); } @@ -720,10 +673,10 @@ public class DrawingManager { if (drawOptions.contains(DrawOption.EXTRA_SPACE)) { float borderThickness = (float) (3f * (zoom > 1 ? 1 : zoom)); g2dIn.setStroke(ObjectCache.getInstance().fetchStroke(borderThickness, - new float[] {borderThickness * 4, borderThickness * 4,}, 0, BasicStroke.CAP_BUTT)); + new float[] {borderThickness * 4, borderThickness * 4,}, 0, BasicStroke.CAP_BUTT)); g2dIn.setColor(theme.getOutlineColor()); extraSpaceRect.setRect(new Rectangle2D.Double(extraSpace, extraSpace, - innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight())); + innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight())); g2dIn.draw(extraSpaceRect); extraSpaceTx.setTransform(g2dIn.getTransform()); @@ -731,7 +684,7 @@ public class DrawingManager { g2dIn.transform(AffineTransform.getTranslateInstance(extraSpace, extraSpace)); if (visibleRect != null) visibleRect.setRect(visibleRect.getX() - extraSpace, visibleRect.getY() - extraSpace, - visibleRect.getWidth(), visibleRect.getHeight()); + visibleRect.getWidth(), visibleRect.getHeight()); } } @@ -740,9 +693,9 @@ public class DrawingManager { if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) { lastStatsReportedTime = System.currentTimeMillis(); String mapAsString = renderStatsByType.entrySet().stream() - .sorted( - (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) - .map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}")); + .sorted( + (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) + .map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}")); LOG.debug("Render stats: " + mapAsString); LOG.debug("Page stats: " + totalStats.toAvgString()); DrawingCache.Instance.logStats(); @@ -797,11 +750,11 @@ public class DrawingManager { double height = project.getHeight().convertToPixels(); double targetExtraSpace = EXTRA_SPACE * Math.max(width, height); return CalcUtils.roundToGrid(targetExtraSpace, - project.getGridSpacing().scale(project.getDotSpacing())); + project.getGridSpacing().scale(project.getDotSpacing())); } public Dimension getCanvasDimensions(Project project, Double zoomLevel, - boolean includeExtraSpace) { + boolean includeExtraSpace) { double width = project.getWidth().convertToPixels(); double height = project.getHeight().convertToPixels(); @@ -838,7 +791,7 @@ public class DrawingManager { currentContinuityAreas = continuityGraphCache.findAreasFor(p); } - + public List getContinuityAreas() { // Find all individual continuity areas for all components List preliminaryAreas = new ArrayList(); @@ -849,17 +802,17 @@ public class DrawingManager { if (a == null || a.getOutlineArea() == null) continue; - + IDIYComponent component = entry.getKey(); - + int layerId; - + if (component instanceof ILayeredComponent) { layerId = ((ILayeredComponent)component).getLayerId(); } else { layerId = 0; } - + Collection positiveAreas = a.getContinuityPositiveAreas(); if (positiveAreas != null) { for (Area a1 : positiveAreas) { @@ -867,7 +820,7 @@ public class DrawingManager { checkBreakout.add(false); } } - + Collection negativeAreas = a.getContinuityNegativeAreas(); if (negativeAreas != null) { for (Area na : negativeAreas) { @@ -892,9 +845,9 @@ public class DrawingManager { areas.add(a); } else { areas.addAll(breakoutAreas.stream() - .map(area -> new ContinuityArea(a.getLayerId(), area)) - .collect(Collectors.toList())); - } + .map(area -> new ContinuityArea(a.getLayerId(), area)) + .collect(Collectors.toList())); + } } else areas.add(a); } @@ -914,7 +867,7 @@ public class DrawingManager { } z++; } - + List areas = getContinuityAreas(); return NetlistBuilder.buildContinuityGraph(areas, connections); @@ -923,9 +876,9 @@ public class DrawingManager { public List getContinuityAreaProximity(float threshold) { List continuityAreas = getContinuityAreas(); Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for - // approx good results + // approx good results List expanded = continuityAreas.parallelStream() - .map((a) -> new Area(s.createStrokedShape(a.getArea()))).collect(Collectors.toList()); + .map((a) -> new Area(s.createStrokedShape(a.getArea()))).collect(Collectors.toList()); List intersections = new ArrayList(); for (int i = 0; i < expanded.size() - 1; i++) { for (int j = i + 1; j < expanded.size(); j++) { @@ -945,13 +898,13 @@ public class DrawingManager { Rectangle2D bounds2d = a.getBounds2D(); if (bounds2d.getWidth() < minSize) bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(), - minSize, bounds2d.getHeight()); + minSize, bounds2d.getHeight()); if (bounds2d.getHeight() < minSize) bounds2d = new Rectangle2D.Double(bounds2d.getX(), bounds2d.getCenterY() - minSize / 2, - bounds2d.getWidth(), minSize); + bounds2d.getWidth(), minSize); shapes.add(new Area(new Ellipse2D.Double(bounds2d.getX(), bounds2d.getY(), - bounds2d.getWidth(), bounds2d.getHeight()))); + bounds2d.getWidth(), bounds2d.getHeight()))); } // AreaUtils.crunchAreas(shapes, null); @@ -960,4 +913,4 @@ public class DrawingManager { return shapes; } -} \ No newline at end of file +} diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java index 766e41f3..f5fac8a0 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/G2DWrapper.java @@ -1,17 +1,17 @@ /* - * + * * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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 * . */ @@ -62,6 +62,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; + import org.diylc.common.ObjectCache; import org.diylc.common.ZoomableStroke; import org.diylc.core.IDrawingObserver; @@ -69,24 +70,24 @@ 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(int)} ()} should be called. After the component is drawn, area may be + * {@link #startedDrawingComponent()} 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. - * + * * @author Branislav Stojkovic */ class G2DWrapper extends Graphics2D implements IDrawingObserver { public static int LINE_SENSITIVITY_MARGIN = 2; - public static int CURVE_SENSITIVITY = 6; + public static int CURVE_SENSITIVITY = 6; private boolean drawingComponent = false; private boolean trackingAllowed = true; private boolean trackingContinuityAllowed = false; private boolean trackingContinuityPositive = true; - + private HashMap trackingStacks = new HashMap(); - + private int uniqueCounter = 0; private Graphics2D canvasGraphics; @@ -104,13 +105,11 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { private Shape lastShape; private double zoom; - private int zOrder; - - private AffineTransform mirrorTransform; + private int zOrder; /** * Creates a wrapper around specified {@link Graphics2D} object. - * + * * @param canvasGraphics */ public G2DWrapper(Graphics2D canvasGraphics, double zoom) { @@ -123,14 +122,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { continuityMarker = null; currentTx = new AffineTransform(); } - + public Graphics2D getCanvasGraphics() { return canvasGraphics; } /** * Clears out the current area and caches canvas settings. - * + * * @param zOrder */ public void startedDrawingComponent(int zOrder) { @@ -154,7 +153,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { /** * Reverts {@link Graphics2D} settings and returns area drawn by component in the meantime. - * + * * @return */ public ComponentArea finishedDrawingComponent() { @@ -164,27 +163,27 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { canvasGraphics.setTransform(originalTx); canvasGraphics.setComposite(originalComposite); canvasGraphics.setFont(originalFont); -// Map sorted = sortByComparator(trackingStacks, false); - return new ComponentArea(zOrder,currentArea, new ArrayList(continuityPositiveAreas.values()), - new ArrayList(continuityNegativeAreas.values())); +// Map sorted = sortByComparator(trackingStacks, false); + return new ComponentArea(zOrder,currentArea, new ArrayList(continuityPositiveAreas.values()), + new ArrayList(continuityNegativeAreas.values())); } - + // start - used for caching - + public Area getCurrentArea() { return currentArea; } - + public Map getContinuityPositiveAreas() { return continuityPositiveAreas; } - + public Map getContinuityNegativeAreas() { return continuityNegativeAreas; } - + // end - used for caching - + public void merge(Area currentArea, Map continuityPositiveAreas, Map continuityNegativeAreas) { this.currentArea = currentArea; this.continuityPositiveAreas = continuityPositiveAreas; @@ -213,22 +212,18 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { } @Override - public boolean isTrackingContinuityArea() { + public boolean isTrackingContinuityArea() { return trackingContinuityAllowed; - } - + } + @Override public void setContinuityMarker(String marker) { - continuityMarker = marker; - } - - public void setMirrorTransform(AffineTransform mirrorTransform) { - this.mirrorTransform = mirrorTransform; - } + continuityMarker = marker; + } /** * Appends shape interior to the current component area. - * + * * @param s */ private void appendShape(Shape s) { @@ -242,7 +237,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { area.transform(currentTx); if (trackingAllowed) - currentArea.add(area); + currentArea.add(area); Map toAdd; if (trackingContinuityAllowed) { @@ -255,18 +250,18 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { toAdd.put(marker, area); // markStack(); } else { - Area current = toAdd.getOrDefault(marker, null); - if (current == null) { + Area current = toAdd.getOrDefault(marker, null); + if (current == null) { toAdd.put(marker, area); -// markStack(); +// markStack(); } else current.add(area); - } + } } lastShape = s; } } - + @SuppressWarnings("unused") private void markStack() { StringWriter sw = new StringWriter(); @@ -280,7 +275,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { /** * Appends shape outline to the current component area. - * + * * @param s */ private void appendShapeOutline(Shape s) { @@ -350,73 +345,22 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { @Override public void drawString(String str, int x, int y) { - this.drawString(str, (float)x, (float)y); + canvasGraphics.drawString(str, x, y); + if (drawingComponent && trackingAllowed) { + FontMetrics fontMetrics = canvasGraphics.getFontMetrics(); + Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics); + Point2D point = new Point2D.Double(x, y); + // currentTx.transform(point, point); + Rectangle2D finalRec = + new Rectangle2D.Double(rect.getX() + point.getX(), rect.getY() + point.getY(), rect.getWidth(), + rect.getHeight()); + appendShape(finalRec); + } } @Override public void drawString(String str, float x, float y) { - if (this.mirrorTransform == null) { - canvasGraphics.drawString(str, x, y); - } else { - drawStringUnmirrored(str, x, y); - } - handleDrawString(str, x, y); - } - - private void drawStringUnmirrored(String str, float x, float y) { - // Save the current transform (T), which includes the mirror. - AffineTransform originalTransform = new AffineTransform(getTransform()); - - // Compute the non-mirrored transform A by "canceling" the mirror: - // Since T = A ? M and M is self-inverse, we have A = T ? M. - AffineTransform nonMirroredTransform = new AffineTransform(originalTransform); - nonMirroredTransform.concatenate(mirrorTransform); - - GlyphVector gv = getFont().createGlyphVector(this.getFontRenderContext(), str); - Rectangle2D visualBounds = gv.getVisualBounds(); - // Use a temporary Graphics2D (with transform A) to obtain correct FontMetrics. - Graphics2D gTemp = (Graphics2D) canvasGraphics.create(); - gTemp.setTransform(nonMirroredTransform); - FontMetrics fm = gTemp.getFontMetrics(); - double textWidth = fm.stringWidth(str); - // Use ascent + descent as the bounding box height. - double textHeight = visualBounds.getHeight();//fm.getAscent() + fm.getDescent(); - double ascent = fm.getAscent(); - gTemp.dispose(); - - // 1. Compute the center of the text?s bounding box in the mirrored (T) space. - Point2D centerMirrored = new Point2D.Double(x + textWidth / 2.0, - y + textHeight / 2.0); - - // 2. Convert that center to screen coordinates using the original transform T. - Point2D centerScreen = originalTransform.transform(centerMirrored, null); - - // 3. Map the screen coordinate into the non-mirrored coordinate system (A). - Point2D centerNonMirrored; - try { - centerNonMirrored = nonMirroredTransform.createInverse().transform(centerScreen, null); - } catch (NoninvertibleTransformException e) { - e.printStackTrace(); - centerNonMirrored = centerMirrored; // Fallback if inversion fails. - } - - // 4. Compute the new top-left in A so that the bounding box (width x height) - // remains centered at centerNonMirrored. - double newTopLeftX = centerNonMirrored.getX() - textWidth / 2.0; - double newTopLeftY = centerNonMirrored.getY() - textHeight / 2.0; - // Adjust for the baseline (drawString uses the baseline for y) - float drawX = (float) newTopLeftX; - float drawY = (float) (newTopLeftY + ascent); - - // 5. Set the transform to the non-mirrored transform (A) so that mirror is canceled. - canvasGraphics.setTransform(nonMirroredTransform); - canvasGraphics.drawString(str, drawX, drawY); - - // 6. Restore the original transform. - setTransform(originalTransform); - } - - private void handleDrawString(String str, float x, float y) { + canvasGraphics.drawString(str, x, y); if (drawingComponent && trackingAllowed) { FontMetrics fontMetrics = canvasGraphics.getFontMetrics(); Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics); @@ -625,7 +569,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { canvasGraphics.drawArc(x, y, width, height, startAngle, arcAngle); if (drawingComponent && (trackingAllowed || trackingContinuityAllowed)) { appendShapeOutline(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); - } + } } @Override @@ -655,14 +599,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, - ImageObserver observer) { + ImageObserver observer) { // FIXME: map return canvasGraphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, - Color bgcolor, ImageObserver observer) { + Color bgcolor, ImageObserver observer) { // FIXME: map return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); } @@ -813,12 +757,12 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { public void setXORMode(Color c1) { canvasGraphics.setXORMode(c1); } - + @SuppressWarnings("unused") private static Map sortByComparator(Map unsortMap, - final boolean order) { + final boolean order) { List> list = - new LinkedList>(unsortMap.entrySet()); + new LinkedList>(unsortMap.entrySet()); // Sorting the list based on values Collections.sort(list, new Comparator>() { @@ -840,4 +784,4 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { return sortedMap; } -} \ No newline at end of file +} diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/transform/CliffJackTransformer.java b/diylc/diylc-library/src/main/java/org/diylc/components/transform/CliffJackTransformer.java index 816f668d..687545e1 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/transform/CliffJackTransformer.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/transform/CliffJackTransformer.java @@ -43,7 +43,7 @@ public class CliffJackTransformer implements IComponentTransformer { @Override public boolean mirroringChangesCircuit() { - return true; + return false; } @Override diff --git a/diylc/diylc-swing/dict.txt b/diylc/diylc-swing/dict.txt index 08cc1da9..4c1b704b 100644 --- a/diylc/diylc-swing/dict.txt +++ b/diylc/diylc-swing/dict.txt @@ -70,6 +70,7 @@ And Angle Angle Offset Anti-Aliasing +Apply Model Arrow Size Author Auto-Create Pads @@ -81,6 +82,8 @@ Below Board Board Color Board Width +Board1 +Board2 Boards Bobbin 1 Bobbin 2 From 90816bebeb86292091ba88c24e611993c53907d1 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Mon, 17 Mar 2025 22:54:23 +0100 Subject: [PATCH 03/32] Update update.xml --- diylc/diylc-core/src/main/resources/update.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index 83876df3..9e795875 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -249,6 +249,10 @@ 2025-03-09 00:00:00.000 CET + + NEW_FEATURE + Added ability to render the underside of boards along with the top side for easier validation, building and debugging + NEW_FEATURE Configurable 'High DPI Rendering' feature which makes the diagram smoother on high-resolution displays but can affect performance From a1c27ef1b23e1501adf99a0f453fe779508f59db Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 20:59:51 +0100 Subject: [PATCH 04/32] transparency for traces --- .../diylc/components/AbstractCurvedComponent.java | 2 +- .../components/AbstractTransparentComponent.java | 5 ++++- .../diylc/components/AbstractLeadedComponent.java | 7 ++++++- .../diylc/components/connectivity/CopperTrace.java | 5 ----- .../diylc/components/connectivity/CurvedTrace.java | 5 ----- .../org/diylc/components/connectivity/Eyelet.java | 14 +++++++++++--- .../diylc/components/connectivity/GroundFill.java | 12 +++++++++--- .../org/diylc/components/connectivity/Jumper.java | 5 ----- .../org/diylc/components/connectivity/Line.java | 5 ----- .../diylc/components/connectivity/SolderPad.java | 13 ++++++++++--- .../diylc/components/connectivity/TraceCut.java | 11 ++++++++--- .../org/diylc/components/connectivity/Turret.java | 11 ++++++++--- 12 files changed, 57 insertions(+), 38 deletions(-) diff --git a/diylc/diylc-core/src/main/java/org/diylc/components/AbstractCurvedComponent.java b/diylc/diylc-core/src/main/java/org/diylc/components/AbstractCurvedComponent.java index 69458fa4..983eb7d0 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/components/AbstractCurvedComponent.java +++ b/diylc/diylc-core/src/main/java/org/diylc/components/AbstractCurvedComponent.java @@ -76,7 +76,7 @@ public abstract class AbstractCurvedComponent extends AbstractTransparentComp * @param curve * @param g2d * @param componentState - * @param theme + * @param drawingObserver */ protected abstract void drawCurve(Path2D curve, Graphics2D g2d, ComponentState componentState, IDrawingObserver drawingObserver); diff --git a/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java b/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java index 78a9615d..52b18e84 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java +++ b/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java @@ -29,10 +29,13 @@ public abstract class AbstractTransparentComponent extends AbstractComponent< public static byte MAX_ALPHA = 127; - protected byte alpha = MAX_ALPHA; + protected Byte alpha = MAX_ALPHA; @EditableProperty public Byte getAlpha() { + if (alpha == null) { + alpha = MAX_ALPHA; + } return alpha; } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java b/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java index cdb8d891..fe9d7d3f 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java @@ -441,7 +441,11 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen stroke = ObjectCache.getInstance().fetchStroke(thickness, new float[] {thickness / 4, thickness * 4}, 0, BasicStroke.CAP_ROUND); break; } - + + Composite oldComposite = g2d.getComposite(); + if (getAlpha() < MAX_ALPHA) { + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA)); + } if (shouldShadeLeads()) { Shape lineShape = stroke.createStrokedShape(line); @@ -463,6 +467,7 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen if (isCopperArea) observer.stopTrackingContinuityArea(); } + g2d.setComposite(oldComposite); } protected LineStyle getStyle() { diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java index baff2381..f0862538 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java @@ -133,11 +133,6 @@ public class CopperTrace extends AbstractLeadedComponent implements ILayer return super.getBorderColor(); } - @Override - public Byte getAlpha() { - return super.getAlpha(); - } - @Override public Size getLength() { return super.getLength(); diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CurvedTrace.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CurvedTrace.java index 56a3608a..0b35f1df 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CurvedTrace.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CurvedTrace.java @@ -111,11 +111,6 @@ public class CurvedTrace extends AbstractCurvedComponent implements ILayer @Override public void setValue(Void value) {} - @Override - public Byte getAlpha() { - return super.getAlpha(); - } - @Override public void setAlpha(Byte alpha) { super.setAlpha(alpha); diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Eyelet.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Eyelet.java index c202a7db..3e74d70c 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Eyelet.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Eyelet.java @@ -21,13 +21,13 @@ */ package org.diylc.components.connectivity; -import java.awt.Color; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.diylc.common.ObjectCache; import org.diylc.components.AbstractComponent; +import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.ComponentState; import org.diylc.core.IDIYComponent; @@ -49,7 +49,7 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class, enableCache = true) -public class Eyelet extends AbstractComponent { +public class Eyelet extends AbstractTransparentComponent { private static final long serialVersionUID = 1L; @@ -71,6 +71,12 @@ public class Eyelet extends AbstractComponent { } int diameter = getClosestOdd((int) size.convertToPixels()); int holeDiameter = getClosestOdd((int) holeSize.convertToPixels()); + + Composite oldComposite = g2d.getComposite(); + if (getAlpha() < MAX_ALPHA) { + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA)); + } + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f)); g2d.setColor(color); drawingObserver.startTrackingContinuityArea(true); @@ -84,6 +90,8 @@ public class Eyelet extends AbstractComponent { g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color.darker()); g2d.drawOval((int)(point.getX() - holeDiameter / 2), (int)(point.getY() - holeDiameter / 2), holeDiameter, holeDiameter); + + g2d.setComposite(oldComposite); } @Override diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/GroundFill.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/GroundFill.java index 0db6d4ac..ac6d255f 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/GroundFill.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/GroundFill.java @@ -21,8 +21,7 @@ */ package org.diylc.components.connectivity; -import java.awt.Color; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Point2D; import java.util.Arrays; import java.util.EnumSet; @@ -33,6 +32,7 @@ import com.bancika.gerberwriter.GerberFunctions; import org.diylc.common.ObjectCache; import org.diylc.common.PCBLayer; import org.diylc.components.AbstractComponent; +import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.ComponentState; import org.diylc.core.IDIYComponent; @@ -52,7 +52,8 @@ import org.diylc.core.measures.SizeUnit; @ComponentDescriptor(name = "Ground Fill", author = "Branislav Stojkovic", category = "Connectivity", instanceNamePrefix = "GF", description = "Polygonal ground fill area", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, transformer = SimpleComponentTransformer.class) -public class GroundFill extends AbstractComponent implements ILayeredComponent, IGerberComponentSimple { +public class GroundFill extends AbstractTransparentComponent + implements ILayeredComponent, IGerberComponentSimple { private static final long serialVersionUID = 1L; @@ -83,7 +84,12 @@ public class GroundFill extends AbstractComponent implements ILayeredCompo yPoints[i] = (int)controlPoints[i].getY(); } drawingObserver.startTrackingContinuityArea(true); + Composite oldComposite = g2d.getComposite(); + if (getAlpha() < MAX_ALPHA) { + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA)); + } g2d.fillPolygon(xPoints, yPoints, controlPoints.length); + g2d.setComposite(oldComposite); drawingObserver.stopTrackingContinuityArea(); // Do not track any changes that follow because the whole board has been // tracked so far. diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java index 2f69dd61..61d281cf 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java @@ -103,11 +103,6 @@ public class Jumper extends AbstractLeadedComponent implements IContinuity return super.getBorderColor(); } - @Override - public Byte getAlpha() { - return super.getAlpha(); - } - @Override public Size getLength() { return super.getLength(); diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java index 0b0234d2..9da327a5 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java @@ -233,11 +233,6 @@ public class Line extends AbstractLeadedComponent { return super.getBorderColor(); } - @Override - public Byte getAlpha() { - return super.getAlpha(); - } - @Override public Size getLength() { return super.getLength(); diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/SolderPad.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/SolderPad.java index e734cca6..ebd05240 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/SolderPad.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/SolderPad.java @@ -17,8 +17,7 @@ */ package org.diylc.components.connectivity; -import java.awt.Color; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -29,6 +28,7 @@ import com.bancika.gerberwriter.GerberFunctions; import org.diylc.common.PCBLayer; import org.diylc.components.AbstractComponent; +import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.ComponentState; import org.diylc.core.IDIYComponent; @@ -55,7 +55,8 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class, enableCache = true) -public class SolderPad extends AbstractComponent implements ILayeredComponent, IGerberComponentCustom { +public class SolderPad extends AbstractTransparentComponent + implements ILayeredComponent, IGerberComponentCustom { private static final long serialVersionUID = 1L; @@ -90,6 +91,11 @@ public class SolderPad extends AbstractComponent implements ILayeredCompon gerberDrawingObserver.startGerberOutput(gerberCopperLayer, GerberFunctions.CONNECTOR_PAD, false); gerberDrawingObserver.startGerberOutput(org.diylc.core.gerber.GerberLayer.SolderMaskBot, "Material", false); } + Composite oldComposite = g2d.getComposite(); + if (getAlpha() < MAX_ALPHA) { + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA)); + } + if (type == Type.ROUND) { g2d.fill(new Ellipse2D.Double(point.getX() - diameter / 2, point.getY() - diameter / 2, diameter, diameter)); @@ -116,6 +122,7 @@ public class SolderPad extends AbstractComponent implements ILayeredCompon g2d.fill(new Ellipse2D.Double(point.getX() - holeDiameter / 2, point.getY() - holeDiameter / 2, holeDiameter, holeDiameter)); } + g2d.setComposite(oldComposite); if (gerberDrawingObserver != null) gerberDrawingObserver.stopGerberOutput(); } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/TraceCut.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/TraceCut.java index 3bc979a3..4cc3964f 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/TraceCut.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/TraceCut.java @@ -21,14 +21,14 @@ */ package org.diylc.components.connectivity; -import java.awt.Color; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.diylc.common.ObjectCache; import org.diylc.common.OrientationHV; import org.diylc.components.AbstractComponent; +import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.boards.AbstractBoard; import org.diylc.components.boards.VeroBoard; import org.diylc.components.transform.SimpleComponentTransformer; @@ -49,7 +49,7 @@ import org.diylc.utils.Constants; description = "Designates the place where a trace on the vero board needs to be cut", instanceNamePrefix = "Cut", zOrder = IDIYComponent.BOARD + 1, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, transformer = SimpleComponentTransformer.class, enableCache = true) -public class TraceCut extends AbstractComponent implements ILayeredComponent { +public class TraceCut extends AbstractTransparentComponent implements ILayeredComponent { private static final long serialVersionUID = 1L; @@ -79,6 +79,10 @@ public class TraceCut extends AbstractComponent implements ILayeredCompone if (checkPointsClipped(g2d.getClip())) { return; } + Composite oldComposite = g2d.getComposite(); + if (getAlpha() < MAX_ALPHA) { + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA)); + } g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(1f)); int size = getClosestOdd((int) this.size.convertToPixels()); int cutWidth = getClosestOdd((int) CUT_WIDTH.convertToPixels()); @@ -105,6 +109,7 @@ public class TraceCut extends AbstractComponent implements ILayeredCompone g2d.setColor(getBoardColor().darker()); g2d.drawOval((int)(point.getX() - holeSize / 2), (int)(point.getY() - holeSize / 2), holeSize, holeSize); } + g2d.setComposite(oldComposite); } @Override diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Turret.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Turret.java index bd5c21f7..9400d75c 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Turret.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Turret.java @@ -21,13 +21,13 @@ */ package org.diylc.components.connectivity; -import java.awt.Color; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.diylc.common.ObjectCache; import org.diylc.components.AbstractComponent; +import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.ComponentState; import org.diylc.core.IDIYComponent; @@ -47,7 +47,7 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class, enableCache = true) -public class Turret extends AbstractComponent { +public class Turret extends AbstractTransparentComponent { private static final long serialVersionUID = 1L; @@ -67,6 +67,10 @@ public class Turret extends AbstractComponent { if (checkPointsClipped(g2d.getClip())) { return; } + Composite oldComposite = g2d.getComposite(); + if (getAlpha() < MAX_ALPHA) { + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * getAlpha() / MAX_ALPHA)); + } int diameter = getClosestOdd((int) size.convertToPixels()); int holeDiameter = getClosestOdd((int) holeSize.convertToPixels()); g2d.setColor(color); @@ -82,6 +86,7 @@ public class Turret extends AbstractComponent { g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color.darker()); g2d.drawOval((int)(point.getX() - holeDiameter / 2), (int)(point.getY() - holeDiameter / 2), holeDiameter, holeDiameter); + g2d.setComposite(oldComposite); } @Override From a2686ae7a00e5e0874f59fe49737bc2a6b19f937 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 21:09:25 +0100 Subject: [PATCH 05/32] Ctrl+X (Command+X) doesn't work because 'Project Explorer' steals the focus on X key press --- diylc/diylc-core/src/main/resources/update.xml | 4 ++++ .../main/java/org/diylc/swing/actions/edit/CopyAction.java | 2 +- .../src/main/java/org/diylc/swing/actions/edit/CutAction.java | 4 ++-- .../java/org/diylc/swing/actions/edit/DuplicateAction.java | 4 ++-- .../org/diylc/swing/actions/edit/EditSelectionAction.java | 2 +- .../main/java/org/diylc/swing/actions/edit/GroupAction.java | 4 ++-- .../main/java/org/diylc/swing/actions/edit/NudgeAction.java | 4 ++-- .../main/java/org/diylc/swing/actions/edit/PasteAction.java | 4 ++-- .../java/org/diylc/swing/actions/edit/SelectAllAction.java | 4 ++-- .../main/java/org/diylc/swing/actions/edit/UngroupAction.java | 2 +- .../main/java/org/diylc/swing/actions/file/ImportAction.java | 4 ++-- .../src/main/java/org/diylc/swing/actions/file/NewAction.java | 4 ++-- .../main/java/org/diylc/swing/actions/file/OpenAction.java | 2 +- .../main/java/org/diylc/swing/actions/file/SaveAction.java | 4 ++-- .../main/java/org/diylc/swing/actions/file/SaveAsAction.java | 4 ++-- .../java/org/diylc/swing/plugins/explorer/ExplorerPlugin.java | 3 +-- .../java/org/diylc/swing/plugins/file/FileMenuPlugin.java | 4 ++-- .../java/org/diylc/swing/plugins/layers/LayersMenuPlugin.java | 2 +- diylc/pom.xml | 2 +- 19 files changed, 33 insertions(+), 30 deletions(-) diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index 9e795875..8c697d6c 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -281,6 +281,10 @@ BUG_FIX Rotating selected components sometimes wacks the alignment off the grid + + BUG_FIX + Ctrl+X (Command+X) doesn't work because 'Project Explorer' steals the focus on X key press + https://github.com/bancika/diy-layout-creator/releases diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CopyAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CopyAction.java index 4ade138c..43c7c7a3 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CopyAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CopyAction.java @@ -28,7 +28,7 @@ public class CopyAction extends AbstractAction { this.clipboardOwner = clipboardOwner; putValue(AbstractAction.NAME, "Copy"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.Copy.getIcon()); } diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CutAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CutAction.java index 714c6eda..048caabe 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CutAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/CutAction.java @@ -28,7 +28,7 @@ public class CutAction extends AbstractAction { this.clipboardOwner = clipboardOwner; putValue(AbstractAction.NAME, "Cut"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.Cut.getIcon()); } @@ -40,4 +40,4 @@ public class CutAction extends AbstractAction { clipboardOwner); plugInPort.deleteSelectedComponents(); } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/DuplicateAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/DuplicateAction.java index f2c59d2e..df8fcd78 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/DuplicateAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/DuplicateAction.java @@ -21,7 +21,7 @@ public class DuplicateAction extends AbstractAction { this.plugInPort = plugInPort; putValue(AbstractAction.NAME, "Duplicate"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_D, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.DocumentsGear.getIcon()); } @@ -34,4 +34,4 @@ public class DuplicateAction extends AbstractAction { ActionFactory.LOG.error("Coule not duplicate.", ex); } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/EditSelectionAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/EditSelectionAction.java index 26a1dd26..667a78e6 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/EditSelectionAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/EditSelectionAction.java @@ -23,7 +23,7 @@ public class EditSelectionAction extends AbstractAction { this.plugInPort = plugInPort; putValue(AbstractAction.NAME, "Edit Selection"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_E, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.EditComponent.getIcon()); } diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/GroupAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/GroupAction.java index 69c556cf..14508884 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/GroupAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/GroupAction.java @@ -21,7 +21,7 @@ public class GroupAction extends AbstractAction { this.plugInPort = plugInPort; putValue(AbstractAction.NAME, "Group Selection"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_G, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.Group.getIcon()); } @@ -30,4 +30,4 @@ public class GroupAction extends AbstractAction { ActionFactory.LOG.info("Group Selection triggered"); plugInPort.groupSelectedComponents(); } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/NudgeAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/NudgeAction.java index d1a9d92a..b052d964 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/NudgeAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/NudgeAction.java @@ -32,7 +32,7 @@ public class NudgeAction extends AbstractAction { putValue(AbstractAction.NAME, "Nudge"); putValue(AbstractAction.SMALL_ICON, IconLoader.FitToSize.getIcon()); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); } @Override @@ -56,4 +56,4 @@ public class NudgeAction extends AbstractAction { plugInPort.nudgeSelection(n.getxOffset(), n.getyOffset(), n.getAffectStuckComponents()); } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/PasteAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/PasteAction.java index 15366fab..24187a85 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/PasteAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/PasteAction.java @@ -27,7 +27,7 @@ public class PasteAction extends AbstractAction { this.clipboard = clipboard; putValue(AbstractAction.NAME, "Paste"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.Paste.getIcon()); } @@ -43,4 +43,4 @@ public class PasteAction extends AbstractAction { ActionFactory.LOG.error("Coule not paste.", ex); } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/SelectAllAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/SelectAllAction.java index c5397742..8df6a8f1 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/SelectAllAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/SelectAllAction.java @@ -21,7 +21,7 @@ public class SelectAllAction extends AbstractAction { this.plugInPort = plugInPort; putValue(AbstractAction.NAME, "Select All"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_A, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.Selection.getIcon()); } @@ -30,4 +30,4 @@ public class SelectAllAction extends AbstractAction { ActionFactory.LOG.info("Select All triggered"); plugInPort.selectAll(0); } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/UngroupAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/UngroupAction.java index 4d1f34b7..88bc3af9 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/UngroupAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/edit/UngroupAction.java @@ -21,7 +21,7 @@ public class UngroupAction extends AbstractAction { this.plugInPort = plugInPort; putValue(AbstractAction.NAME, "Ungroup Selection"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_U, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.Ungroup.getIcon()); } diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/ImportAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/ImportAction.java index 1133157e..f2f07987 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/ImportAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/ImportAction.java @@ -66,7 +66,7 @@ public class ImportAction extends AbstractAction { }, InMemoryConfigurationManager.getInstance()); putValue(AbstractAction.NAME, "Import"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_I, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.ElementInto.getIcon()); } @@ -107,4 +107,4 @@ public class ImportAction extends AbstractAction { }, true); } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/NewAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/NewAction.java index a254e1bf..60b2dde2 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/NewAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/NewAction.java @@ -28,7 +28,7 @@ public class NewAction extends AbstractAction { this.plugInPort = plugInPort; putValue(AbstractAction.NAME, "New"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.DocumentPlain.getIcon()); } @@ -54,4 +54,4 @@ public class NewAction extends AbstractAction { } } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/OpenAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/OpenAction.java index 650d7e2c..ab257edc 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/OpenAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/OpenAction.java @@ -28,7 +28,7 @@ public class OpenAction extends AbstractAction { this.swingUI = swingUI; putValue(AbstractAction.NAME, "Open"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.FolderOut.getIcon()); } diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAction.java index 56a55153..fea6a5c2 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAction.java @@ -28,7 +28,7 @@ public class SaveAction extends AbstractAction { this.swingUI = swingUI; putValue(AbstractAction.NAME, "Save"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); putValue(AbstractAction.SMALL_ICON, IconLoader.DiskBlue.getIcon()); } @@ -79,4 +79,4 @@ public class SaveAction extends AbstractAction { }, true); } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAsAction.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAsAction.java index 4509101f..bda4a0b8 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAsAction.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/actions/file/SaveAsAction.java @@ -29,7 +29,7 @@ public class SaveAsAction extends AbstractAction { this.swingUI = swingUI; putValue(AbstractAction.NAME, "Save As"); putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | ActionEvent.SHIFT_MASK)); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | ActionEvent.SHIFT_MASK)); putValue(AbstractAction.SMALL_ICON, IconLoader.DiskBlue.getIcon()); } @@ -63,4 +63,4 @@ public class SaveAsAction extends AbstractAction { }, true); } } -} \ No newline at end of file +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/explorer/ExplorerPlugin.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/explorer/ExplorerPlugin.java index e50f8bc0..b12ff860 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/explorer/ExplorerPlugin.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/explorer/ExplorerPlugin.java @@ -90,8 +90,7 @@ public class ExplorerPlugin implements IPlugIn { @Override public boolean dispatchKeyEvent(KeyEvent e) { - if ((canvasPanel.hasFocus() || explorerPane.hasFocus()) - && e.getKeyChar() == 'x' + if (e.getKeyChar() == 'x' && e.getModifiersEx() == 0 && ConfigurationManager.getInstance() .readBoolean(ConfigPlugin.PROJECT_EXPLORER, true)) { explorerPane.getSearchField().requestFocusInWindow(); diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/file/FileMenuPlugin.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/file/FileMenuPlugin.java index afc13691..9c2047c2 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/file/FileMenuPlugin.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/file/FileMenuPlugin.java @@ -86,7 +86,7 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler { swingUI.injectMenuAction( actionFactory.createExportGerberAction(plugInPort, swingUI), EXPORT_TITLE); swingUI.injectMenuAction(actionFactory.createPrintAction(drawingProvider, swingUI, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), FILE_TITLE); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()), FILE_TITLE); swingUI.injectSubmenu(TRACE_MASK_TITLE, IconLoader.TraceMask.getIcon(), FILE_TITLE); swingUI.injectMenuAction(actionFactory.createExportPDFAction(plugInPort, traceMaskDrawingProvider, swingUI, " (mask)"), TRACE_MASK_TITLE); @@ -94,7 +94,7 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler { traceMaskDrawingProvider, swingUI, " (mask)"), TRACE_MASK_TITLE); swingUI.injectMenuAction( actionFactory.createPrintAction(traceMaskDrawingProvider, swingUI, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | KeyEvent.SHIFT_DOWN_MASK), + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | KeyEvent.SHIFT_DOWN_MASK), TRACE_MASK_TITLE); swingUI.injectSubmenu(ANALYZE_TITLE, IconLoader.Scientist.getIcon(), FILE_TITLE); diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/layers/LayersMenuPlugin.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/layers/LayersMenuPlugin.java index a4079c5c..9f315e9e 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/layers/LayersMenuPlugin.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/layers/LayersMenuPlugin.java @@ -79,7 +79,7 @@ public class LayersMenuPlugin implements IPlugIn { }; visibleAction.putValue(IView.CHECK_BOX_MENU_ITEM, true); visibleAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_0 + zOrder, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); visibleActionMap.put(layer, visibleAction); AbstractAction selectAllAction = new AbstractAction("Select All") { diff --git a/diylc/pom.xml b/diylc/pom.xml index 0a778581..8eb98ec3 100644 --- a/diylc/pom.xml +++ b/diylc/pom.xml @@ -19,7 +19,7 @@ - ISO-8859-1 + UTF-8 17 17 1.2.15 From 753b8bff8d6f52c4ae05fca96cd58d61cc1f1212 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 21:58:54 +0100 Subject: [PATCH 06/32] improve icon rendering --- .../src/main/java/org/diylc/presenter/ComponentProcessor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java index 2bab6e2b..d5a081af 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java @@ -133,6 +133,10 @@ public class ComponentProcessor { Graphics2D g2d = (Graphics2D) image.getGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); // g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, // RenderingHints.VALUE_STROKE_PURE); componentInstance.drawIcon(g2d, Presenter.ICON_SIZE, Presenter.ICON_SIZE); From 9d1479cd823eedf0c7a4e9b87f0a61192ffcf871 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 21:59:56 +0100 Subject: [PATCH 07/32] Update update.xml --- diylc/diylc-core/src/main/resources/update.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index 8c697d6c..b660b532 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -273,6 +273,10 @@ IMPROVEMENT Omit boards, wires, traces, terminals, etc from Spice netlist output + + IMPROVEMENT + Slightly improved rendering quality of component icons + BUG_FIX Moving selected components with a mouse doesn't register file modification action and doesn't enable Undo action From f6863fb6286914584a1ac8528cfd5a06ab9bd292 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 22:03:28 +0100 Subject: [PATCH 08/32] cleanup --- diylc/diylc-core/.classpath | 67 ------------------------------ diylc/diylc-core/.project | 34 --------------- diylc/diylc-core/config.properties | 2 - diylc/diylc-library/.classpath | 16 ------- diylc/diylc-library/.project | 34 --------------- diylc/diylc-swing/.classpath | 30 ------------- diylc/diylc-swing/.project | 34 --------------- 7 files changed, 217 deletions(-) delete mode 100644 diylc/diylc-core/.classpath delete mode 100644 diylc/diylc-core/.project delete mode 100644 diylc/diylc-core/config.properties delete mode 100644 diylc/diylc-library/.classpath delete mode 100644 diylc/diylc-library/.project delete mode 100644 diylc/diylc-swing/.classpath delete mode 100644 diylc/diylc-swing/.project diff --git a/diylc/diylc-core/.classpath b/diylc/diylc-core/.classpath deleted file mode 100644 index ad4f9a9e..00000000 --- a/diylc/diylc-core/.classpath +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/diylc/diylc-core/.project b/diylc/diylc-core/.project deleted file mode 100644 index 8ea4afa3..00000000 --- a/diylc/diylc-core/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - diylc-core - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - - - - 1741597028960 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/diylc/diylc-core/config.properties b/diylc/diylc-core/config.properties deleted file mode 100644 index c6abd215..00000000 --- a/diylc/diylc-core/config.properties +++ /dev/null @@ -1,2 +0,0 @@ -#org.diylc.utils.Constants.CANVAS_COLOR=#005D96 -#org.diylc.utils.Constants.GRID_COLOR=#FFFFFF \ No newline at end of file diff --git a/diylc/diylc-library/.classpath b/diylc/diylc-library/.classpath deleted file mode 100644 index 05a589ec..00000000 --- a/diylc/diylc-library/.classpath +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/diylc/diylc-library/.project b/diylc/diylc-library/.project deleted file mode 100644 index 0f0c953c..00000000 --- a/diylc/diylc-library/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - diylc-library - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - - - - 1741597247360 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/diylc/diylc-swing/.classpath b/diylc/diylc-swing/.classpath deleted file mode 100644 index cd173d6f..00000000 --- a/diylc/diylc-swing/.classpath +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/diylc/diylc-swing/.project b/diylc/diylc-swing/.project deleted file mode 100644 index 33001697..00000000 --- a/diylc/diylc-swing/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - diylc-swing - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - - - - 1741597247368 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - From a5a4a3632f1c53eb310fedcac8108cecd833eeb0 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 22:12:52 +0100 Subject: [PATCH 09/32] refactor --- .../diylc/presenter/ComponentProcessor.java | 58 +++++++++++++++++-- .../java/org/diylc/presenter/Presenter.java | 48 ++------------- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java index d5a081af..6f151d60 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/ComponentProcessor.java @@ -26,12 +26,8 @@ import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Modifier; +import java.util.*; import javax.swing.Icon; import javax.swing.ImageIcon; import org.apache.log4j.Logger; @@ -47,6 +43,11 @@ import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.KeywordPolicy; import org.diylc.lang.LangUtil; +import org.reflections.Reflections; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; + +import static org.reflections.scanners.Scanners.TypesAnnotated; /** * Utility class with component processing methods. @@ -63,6 +64,7 @@ public class ComponentProcessor { private Map propertyValidatorCache; private Map componentTypeMap; private Map componentTransformerMap; + private Map> componentTypes; public static ComponentProcessor getInstance() { if (instance == null) { @@ -218,6 +220,50 @@ public class ComponentProcessor { return result; } + public Map> getComponentTypes() { + if (componentTypes == null) { + LOG.info("Loading component types."); + componentTypes = new HashMap>(); + Reflections reflections = new Reflections( + new ConfigurationBuilder() + .forPackage("org.diylc") + .filterInputsBy(new FilterBuilder().includePackage("org.diylc.components")) + .setScanners(TypesAnnotated)); + Set> componentTypeClasses = null; + try { + componentTypeClasses = reflections.getTypesAnnotatedWith(ComponentDescriptor.class, false); + + for (Class clazz : componentTypeClasses) { + if (!Modifier.isAbstract(clazz.getModifiers()) && IDIYComponent.class.isAssignableFrom(clazz)) { + ComponentType componentType = + ComponentProcessor.getInstance().extractComponentTypeFrom((Class>) clazz); + if (componentType == null) + continue; + + // just to store in the cache + ComponentProcessor.getInstance().extractProperties(clazz); + + List nestedList; + if (componentTypes.containsKey(componentType.getCategory())) { + nestedList = componentTypes.get(componentType.getCategory()); + } else { + nestedList = new ArrayList(); + componentTypes.put(componentType.getCategory(), nestedList); + } + nestedList.add(componentType); + } + } + + for (Map.Entry> e : componentTypes.entrySet()) { + LOG.debug(e.getKey() + ": " + e.getValue()); + } + } catch (Exception e) { + LOG.error("Error loading component types", e); + } + } + return componentTypes; + } + /** * Returns properties that have the same value for all the selected components. * diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java index a57686b7..aca59d0b 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java @@ -136,6 +136,9 @@ public class Presenter implements IPlugInPort, IConfigListener { private static final String APPLY_ERROR = LangUtil.translate("Could not apply changes. Check the log for details."); private static final Logger LOG = Logger.getLogger(Presenter.class); + + public static final Double[] ZOOM_LEVELS = + {0.25d, 0.3333d, 0.5d, 0.6667d, 0.75d, 1d, 1.25d, 1.5d, 2d, 2.5d, 3d}; public static VersionNumber CURRENT_VERSION = null; private static List RECENT_VERSIONS = null; @@ -149,7 +152,6 @@ public class Presenter implements IPlugInPort, IConfigListener { private static final int MAX_RECENT_FILES = 20; private Project currentProject; - private Map> componentTypes; // Maps component class names to ComponentType objects. private List plugIns; @@ -242,7 +244,7 @@ public class Presenter implements IPlugInPort, IConfigListener { @Override public java.lang.Double[] getAvailableZoomLevels() { - return new java.lang.Double[] {0.25d, 0.3333d, 0.5d, 0.6667d, 0.75d, 1d, 1.25d, 1.5d, 2d, 2.5d, 3d}; + return ZOOM_LEVELS; } @Override @@ -424,47 +426,7 @@ public class Presenter implements IPlugInPort, IConfigListener { @SuppressWarnings("unchecked") @Override public Map> getComponentTypes() { - if (componentTypes == null) { - LOG.info("Loading component types."); - componentTypes = new HashMap>(); - Reflections reflections = new Reflections( - new ConfigurationBuilder() - .forPackage("org.diylc") - .filterInputsBy(new FilterBuilder().includePackage("org.diylc.components")) - .setScanners(TypesAnnotated)); - Set> componentTypeClasses = null; - try { - componentTypeClasses = reflections.getTypesAnnotatedWith(ComponentDescriptor.class, false); - - for (Class clazz : componentTypeClasses) { - if (!Modifier.isAbstract(clazz.getModifiers()) && IDIYComponent.class.isAssignableFrom(clazz)) { - ComponentType componentType = - ComponentProcessor.getInstance().extractComponentTypeFrom((Class>) clazz); - if (componentType == null) - continue; - - // just to store in the cache - ComponentProcessor.getInstance().extractProperties(clazz); - - List nestedList; - if (componentTypes.containsKey(componentType.getCategory())) { - nestedList = componentTypes.get(componentType.getCategory()); - } else { - nestedList = new ArrayList(); - componentTypes.put(componentType.getCategory(), nestedList); - } - nestedList.add(componentType); - } - } - - for (Map.Entry> e : componentTypes.entrySet()) { - LOG.debug(e.getKey() + ": " + e.getValue()); - } - } catch (Exception e) { - LOG.error("Error loading component types", e); - } - } - return componentTypes; + return ComponentProcessor.getInstance().getComponentTypes(); } @SuppressWarnings({"unchecked"}) From add6bae018d756e4174628be4de73c2990b6d407 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 22:19:41 +0100 Subject: [PATCH 10/32] fixes --- .../diylc-core/src/main/java/org/diylc/presenter/Presenter.java | 2 +- diylc/diylc-swing/pom.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java index aca59d0b..a6eaa454 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java @@ -136,7 +136,7 @@ public class Presenter implements IPlugInPort, IConfigListener { private static final String APPLY_ERROR = LangUtil.translate("Could not apply changes. Check the log for details."); private static final Logger LOG = Logger.getLogger(Presenter.class); - + public static final Double[] ZOOM_LEVELS = {0.25d, 0.3333d, 0.5d, 0.6667d, 0.75d, 1d, 1.25d, 1.5d, 2d, 2.5d, 3d}; diff --git a/diylc/diylc-swing/pom.xml b/diylc/diylc-swing/pom.xml index 7e9de833..2846e3e0 100644 --- a/diylc/diylc-swing/pom.xml +++ b/diylc/diylc-swing/pom.xml @@ -238,6 +238,7 @@ linux-appimage ${project.build.directory}/AppDir + ${appdir.dir}/usr/bin/jre 17.0.14+7 17.0.14_7 OpenJDK17U-jre_x64_linux_hotspot_${jdk.version.file}.tar.gz From e54cfa502aab9303ec1dd38e7920f4becc410a49 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 22:47:58 +0100 Subject: [PATCH 11/32] 'Mini Toggle Switch' ends up misaligned after rotating together with other components --- .../diylc-core/src/main/resources/update.xml | 4 ++ .../MiniToggleSwitchTransformer.java | 49 ++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index b660b532..b2c8d556 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -277,6 +277,10 @@ IMPROVEMENT Slightly improved rendering quality of component icons + + BUG_FIX + 'Mini Toggle Switch' ends up misaligned after rotating together with other components + BUG_FIX Moving selected components with a mouse doesn't register file modification action and doesn't enable Undo action diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/transform/MiniToggleSwitchTransformer.java b/diylc/diylc-library/src/main/java/org/diylc/components/transform/MiniToggleSwitchTransformer.java index 21da24ee..f347f9f2 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/transform/MiniToggleSwitchTransformer.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/transform/MiniToggleSwitchTransformer.java @@ -48,14 +48,26 @@ public class MiniToggleSwitchTransformer implements IComponentTransformer { @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); - } - MiniToggleSwitch sw = (MiniToggleSwitch) component; + + // Calculate center of all control points + double sumX = 0, sumY = 0; + for (int i = 0; i < sw.getControlPointCount(); i++) { + Point2D p = sw.getControlPoint(i); + sumX += p.getX(); + sumY += p.getY(); + } + Point2D controlCenter = new Point2D.Double( + sumX / sw.getControlPointCount(), + sumY / sw.getControlPointCount() + ); + + // Calculate where control center should end up after rotation + Point2D targetCenter = new Point2D.Double(); + AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2 * direction, center.getX(), center.getY()); + rotate.transform(controlCenter, targetCenter); + + // Update orientation first - this will rotate points around first point OrientationHV o = sw.getOrientation(); int oValue = o.ordinal(); oValue += direction; @@ -65,6 +77,29 @@ public class MiniToggleSwitchTransformer implements IComponentTransformer { oValue = 0; o = OrientationHV.values()[oValue]; sw.setOrientation(o); + + // Recalculate current control center after orientation change + sumX = 0; + sumY = 0; + for (int i = 0; i < sw.getControlPointCount(); i++) { + Point2D p = sw.getControlPoint(i); + sumX += p.getX(); + sumY += p.getY(); + } + Point2D newControlCenter = new Point2D.Double( + sumX / sw.getControlPointCount(), + sumY / sw.getControlPointCount() + ); + + // Calculate and apply translation to move control center to target position + double dx = targetCenter.getX() - newControlCenter.getX(); + double dy = targetCenter.getY() - newControlCenter.getY(); + + // Apply translation to all points + for (int i = 0; i < sw.getControlPointCount(); i++) { + Point2D p = sw.getControlPoint(i); + sw.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i); + } } @Override From d545b895d6fef2970a1b2cf17fdfb936581acca8 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Tue, 18 Mar 2025 23:04:24 +0100 Subject: [PATCH 12/32] Update update.xml --- diylc/diylc-core/src/main/resources/update.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index b2c8d556..4fb0b8e6 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -291,7 +291,7 @@ BUG_FIX - Ctrl+X (Command+X) doesn't work because 'Project Explorer' steals the focus on X key press + Ctrl+X (Command+X) shortcut for Cut action doesn't work because 'Project Explorer' steals the focus on X key press https://github.com/bancika/diy-layout-creator/releases From 57594af69d8c981f159e49d440b3dbee57496962 Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Wed, 19 Mar 2025 15:04:54 +0100 Subject: [PATCH 13/32] tests --- .github/workflows/tests.yml | 5 ++++- diylc/diylc-swing/RegressionTestRunner.launch | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8553a0fb..82e47bcd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,6 +9,9 @@ on: jobs: test: runs-on: ubuntu-latest + defaults: + run: + working-directory: diylc steps: - uses: actions/checkout@v3 @@ -36,7 +39,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: coverage-report - path: target/site/jacoco/ + path: diylc/target/site/jacoco/ retention-days: 5 # Optional: Comment test results on PR diff --git a/diylc/diylc-swing/RegressionTestRunner.launch b/diylc/diylc-swing/RegressionTestRunner.launch index 28ecf071..84440f75 100644 --- a/diylc/diylc-swing/RegressionTestRunner.launch +++ b/diylc/diylc-swing/RegressionTestRunner.launch @@ -7,9 +7,11 @@ + + From f512263c2d256e68c73d2e89509fc632d7fd36bd Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Thu, 20 Mar 2025 19:58:09 +0100 Subject: [PATCH 14/32] Update update.xml --- .../diylc-core/src/main/resources/update.xml | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index d6e57062..2b5d3745 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -246,13 +246,9 @@ 1 0 - 2025-03-09 00:00:00.000 CET + 2025-03-20 00:00:00.000 CET - - NEW_FEATURE - Added ability to render the underside of boards along with the top side for easier validation, building and debugging - NEW_FEATURE Configurable 'High DPI Rendering' feature which makes the diagram smoother on high-resolution displays but can affect performance @@ -261,10 +257,6 @@ IMPROVEMENT Significantly improved rendering performance - - IMPROVEMENT - Significantly improved rendering performance - IMPROVEMENT Upgraded source code structure to modern standards and introduced Maven @@ -279,11 +271,7 @@ IMPROVEMENT - Slightly improved rendering quality of component icons - - - BUG_FIX - 'Mini Toggle Switch' ends up misaligned after rotating together with other components + Updated the windows installer to avoid false positive virus detection BUG_FIX @@ -293,6 +281,22 @@ BUG_FIX Rotating selected components sometimes wacks the alignment off the grid + + https://github.com/bancika/diy-layout-creator/releases + + + + 5 + 2 + 0 + + 2025-03-20 00:00:00.000 CET + + + + NEW_FEATURE + Added ability to render the underside of boards along with the top side for easier validation, building and debugging + BUG_FIX Ctrl+X (Command+X) shortcut for Cut action doesn't work because 'Project Explorer' steals the focus on X key press From 6352dc2f0e5a5feecb6e3f486d58190d8c0c3f8b Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Fri, 21 Mar 2025 11:55:51 +0100 Subject: [PATCH 15/32] build linux --- diylc/diylc-swing/pom.xml | 139 ++++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 43 deletions(-) diff --git a/diylc/diylc-swing/pom.xml b/diylc/diylc-swing/pom.xml index 95c2456b..e88370e3 100644 --- a/diylc/diylc-swing/pom.xml +++ b/diylc/diylc-swing/pom.xml @@ -260,47 +260,6 @@ - - sign-jar-launcher - package - - run - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -539,9 +498,17 @@ - + + + + + + + + + - + @@ -703,6 +670,49 @@ + + + sign-jar-launcher + package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + create-mac-bundle @@ -973,6 +983,49 @@ + + + sign-jar-launcher + package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + create-mac-bundle From aeea503d7eafd5cdf30e7c0ac88d5eb7b701011e Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Fri, 21 Mar 2025 11:56:03 +0100 Subject: [PATCH 16/32] build linux --- .../diylc-swing/deploy/linux/test-appimage.sh | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 diylc/diylc-swing/deploy/linux/test-appimage.sh diff --git a/diylc/diylc-swing/deploy/linux/test-appimage.sh b/diylc/diylc-swing/deploy/linux/test-appimage.sh new file mode 100755 index 00000000..b8de95c5 --- /dev/null +++ b/diylc/diylc-swing/deploy/linux/test-appimage.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# Build the AppImage +echo "Building AppImage..." +cd ../.. +#mvn clean package -P linux-appimage + +# Check if build was successful +if [ $? -ne 0 ]; then + echo "Build failed!" + exit 1 +fi + +# Get the AppImage path +APPIMAGE="target/DIYLayoutCreator-5.1.0-x86_64.AppImage" +ZIP_FILE="target/diylc-5.1.0-linux.zip" + +# Check if files exist +if [ ! -f "$APPIMAGE" ]; then + echo "AppImage not found at $APPIMAGE" + exit 1 +fi + +if [ ! -f "$ZIP_FILE" ]; then + echo "Zip file not found at $ZIP_FILE" + exit 1 +fi + +# Create test directory +TEST_DIR="target/appimage-test" +rm -rf "$TEST_DIR" +mkdir -p "$TEST_DIR" + +# Copy files to test directory +echo "Copying files to test directory..." +cp "$APPIMAGE" "$TEST_DIR/" +cp "$ZIP_FILE" "$TEST_DIR/" + +# Extract zip file +echo "Extracting zip file..." +cd "$TEST_DIR" +unzip "diylc-5.1.0-linux.zip" + +# Make AppImage executable +chmod +x "DIYLayoutCreator-5.1.0-x86_64.AppImage" + +# Test AppImage structure +echo "Testing AppImage structure..." +if [ -f "DIYLayoutCreator-5.1.0-x86_64.AppImage" ]; then + echo "AppImage file exists and is executable" + echo "AppImage size: $(ls -lh DIYLayoutCreator-5.1.0-x86_64.AppImage | awk '{print $5}')" + echo "AppImage permissions: $(ls -l DIYLayoutCreator-5.1.0-x86_64.AppImage | awk '{print $1}')" + + # Check if AppImage has the correct magic number + if file "DIYLayoutCreator-5.1.0-x86_64.AppImage" | grep -q "ELF 64-bit LSB executable"; then + echo "AppImage has correct ELF format" + else + echo "Warning: AppImage might not have correct ELF format" + fi + + # Check if AppImage has the correct AppImage magic bytes + if dd if="DIYLayoutCreator-5.1.0-x86_64.AppImage" bs=8 count=1 2>/dev/null | grep -q "AI"; then + echo "AppImage has correct AppImage magic bytes" + else + echo "Warning: AppImage might not have correct AppImage magic bytes" + fi + + echo "AppImage structure test completed successfully!" +else + echo "AppImage file not found in test directory!" + exit 1 +fi \ No newline at end of file From a011a080f4e4b1d00210cd8e893939291c398db3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 11:58:46 +0100 Subject: [PATCH 17/32] Bump junit:junit from 4.7 to 4.13.1 in /diylc (#896) Bumps [junit:junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1) --- updated-dependencies: - dependency-name: junit:junit dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- diylc/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diylc/pom.xml b/diylc/pom.xml index 0a778581..c292cf9f 100644 --- a/diylc/pom.xml +++ b/diylc/pom.xml @@ -61,7 +61,7 @@ junit junit - 4.7 + 4.13.1 test From c4fb3ad30588e7f9b9d16a602aad5b4e90a580eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 11:59:08 +0100 Subject: [PATCH 18/32] Bump com.thoughtworks.xstream:xstream from 1.4.19 to 1.4.21 in /diylc (#895) Bumps [com.thoughtworks.xstream:xstream](https://github.com/x-stream/xstream) from 1.4.19 to 1.4.21. - [Release notes](https://github.com/x-stream/xstream/releases) - [Commits](https://github.com/x-stream/xstream/commits) --- updated-dependencies: - dependency-name: com.thoughtworks.xstream:xstream dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- diylc/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diylc/pom.xml b/diylc/pom.xml index c292cf9f..8f852003 100644 --- a/diylc/pom.xml +++ b/diylc/pom.xml @@ -23,7 +23,7 @@ 17 17 1.2.15 - 1.4.19 + 1.4.21 From f1af933f6d93a4439c75741866affd7aca55ccce Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Fri, 21 Mar 2025 12:18:17 +0100 Subject: [PATCH 19/32] up to 1gb --- diylc/diylc-swing/deploy/linux/run.sh | 17 ++++++++++++++++- diylc/diylc-swing/deploy/win/launch4j-x64.xml | 2 +- diylc/diylc-swing/pom.xml | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/diylc/diylc-swing/deploy/linux/run.sh b/diylc/diylc-swing/deploy/linux/run.sh index 7f78069a..943efe7f 100755 --- a/diylc/diylc-swing/deploy/linux/run.sh +++ b/diylc/diylc-swing/deploy/linux/run.sh @@ -2,4 +2,19 @@ JAVA_BIN="$(which java)" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" CLASS_PATH="${DIR}/diylc.jar" -exec java -Xms512m -Xmx4096m -Dorg.diylc.scriptRun=true -Dfile.encoding=UTF-8 -cp "$CLASS_PATH" --add-exports java.desktop/com.apple.eawt.event=ALL-UNNAMED --add-exports java.desktop/com.apple.eio=ALL-UNNAMED --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 org.diylc.DIYLCStarter "$@" \ No newline at end of file + +exec java \ + -Xms1024m \ + -Xmx4096m \ + -Dorg.diylc.scriptRun=true \ + -Dfile.encoding=UTF-8 \ + -cp "$CLASS_PATH" \ + --add-exports java.desktop/com.apple.eawt.event=ALL-UNNAMED \ + --add-exports java.desktop/com.apple.eio=ALL-UNNAMED \ + --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 \ + org.diylc.DIYLCStarter "$@" \ No newline at end of file diff --git a/diylc/diylc-swing/deploy/win/launch4j-x64.xml b/diylc/diylc-swing/deploy/win/launch4j-x64.xml index aa8e8e3a..d2740d62 100644 --- a/diylc/diylc-swing/deploy/win/launch4j-x64.xml +++ b/diylc/diylc-swing/deploy/win/launch4j-x64.xml @@ -24,7 +24,7 @@ true 17 - 512 + 1024 4096 -Dorg.diylc.scriptRun=true -Dfile.encoding=UTF-8 diff --git a/diylc/diylc-swing/pom.xml b/diylc/diylc-swing/pom.xml index e88370e3..c79eaded 100644 --- a/diylc/diylc-swing/pom.xml +++ b/diylc/diylc-swing/pom.xml @@ -750,7 +750,7 @@ -