From ccf5d207131894b0abbf564b4be73ae9df81d920 Mon Sep 17 00:00:00 2001 From: Branislav Stojkovic Date: Fri, 14 Mar 2025 11:23:56 +0100 Subject: [PATCH] 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