This commit is contained in:
Branislav Stojkovic
2025-03-14 11:23:56 +01:00
parent 08ca6cef4e
commit ccf5d20713
10 changed files with 581 additions and 321 deletions
@@ -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();
}
}
@@ -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();
}
@@ -13,16 +13,15 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.bancika.gerberwriter.DataLayer;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IBoard;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IView; import org.diylc.core.IView;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.measures.Size; import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit; import org.diylc.core.measures.SizeUnit;
import org.diylc.lang.LangUtil; import org.diylc.lang.LangUtil;
import com.bancika.gerberwriter.DataLayer;
public class GerberExporter { public class GerberExporter {
@@ -30,44 +29,47 @@ public class GerberExporter {
private static final Logger LOG = Logger.getLogger(GerberExporter.class); private static final Logger LOG = Logger.getLogger(GerberExporter.class);
public static void exportGerber(String fileNameBase, Project currentProject, IView view, public static void exportGerber(String fileNameBase, Project currentProject, IView view,
Graphics2D graphics2d, String diylcVersion) { Graphics2D graphics2d, String diylcVersion) {
List<IGerberBoard> gerberBoards =
currentProject.getComponents().stream().filter(c -> c instanceof IGerberBoard)
.map(x -> (IGerberBoard) x).collect(Collectors.toList());
Map<IGerberBoard, List<IDIYComponent<?>>> boardComponentMap = List<IBoard> gerberBoards =
new HashMap<IGerberBoard, List<IDIYComponent<?>>>(); currentProject.getComponents().stream()
.filter(IBoard.class::isInstance)
.map(IBoard.class::cast)
.filter(IBoard::shouldExportToGerber).collect(Collectors.toList());
Map<IBoard, List<IDIYComponent<?>>> boardComponentMap =
new HashMap<IBoard, List<IDIYComponent<?>>>();
// DefaultGerberBoard defaultGerberBoard = new DefaultGerberBoard(); // DefaultGerberBoard defaultGerberBoard = new DefaultGerberBoard();
List<IDIYComponent<?>> componentsWithoutBoard = new ArrayList<IDIYComponent<?>>(); List<IDIYComponent<?>> componentsWithoutBoard = new ArrayList<IDIYComponent<?>>();
currentProject.getComponents().stream().filter(c -> c instanceof IGerberComponent) currentProject.getComponents().stream().filter(c -> c instanceof IGerberComponent)
.forEach(c -> { .forEach(c -> {
Point2D firstPoint = c.getControlPoint(0); Point2D firstPoint = c.getControlPoint(0);
IGerberBoard board = gerberBoards.stream() IBoard board = gerberBoards.stream()
.filter(b -> b.getBoardRectangle().contains(firstPoint)).findFirst().orElse(null); .filter(b -> b.getBoardRectangle().contains(firstPoint)).findFirst().orElse(null);
if (board == null) { if (board == null) {
componentsWithoutBoard.add(c); componentsWithoutBoard.add(c);
} else { } else {
boardComponentMap.computeIfAbsent(board, k -> new ArrayList<IDIYComponent<?>>()).add(c); boardComponentMap.computeIfAbsent(board, k -> new ArrayList<IDIYComponent<?>>()).add(c);
} }
}); });
// defaultGerberBoard.setComponents(boardComponentMap.get(defaultGerberBoard)); // defaultGerberBoard.setComponents(boardComponentMap.get(defaultGerberBoard));
if (boardComponentMap.isEmpty()) { if (boardComponentMap.isEmpty()) {
view.showMessage(LangUtil.translate("Nothing to export."), view.showMessage(LangUtil.translate("Nothing to export."),
LangUtil.translate("Gerber Export"), IView.WARNING_MESSAGE); LangUtil.translate("Gerber Export"), IView.WARNING_MESSAGE);
return; return;
} }
if (componentsWithoutBoard.size() > 0) { if (componentsWithoutBoard.size() > 0) {
if (view.showConfirmDialog(String.format( if (view.showConfirmDialog(String.format(
LangUtil.translate("There are some components that are outside of the bounds of boards.%s" 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?"), + "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, "\n", "\n"), LangUtil.translate("Gerber Export"), IView.YES_NO_OPTION,
IView.WARNING_MESSAGE) != IView.YES_OPTION) { IView.WARNING_MESSAGE) != IView.YES_OPTION) {
return; return;
} }
} }
@@ -88,15 +90,15 @@ public class GerberExporter {
String fileNameOutline = GerberLayer.Outline.formatFileName(fileNameBase, boardComponent.getName()); String fileNameOutline = GerberLayer.Outline.formatFileName(fileNameBase, boardComponent.getName());
try { try {
LOG.info(String.format(LangUtil.translate("Exporting outline for board %s to file %s"), LOG.info(String.format(LangUtil.translate("Exporting outline for board %s to file %s"),
boardComponent.getName(), fileNameOutline)); boardComponent.getName(), fileNameOutline));
outlineLayer.dumpGerberToFile(fileNameOutline); outlineLayer.dumpGerberToFile(fileNameOutline);
generatedFiles.add(fileNameOutline); generatedFiles.add(fileNameOutline);
} catch (IOException e) { } catch (IOException e) {
LOG.error("Error writing gerber file: " + e.getMessage()); LOG.error("Error writing gerber file: " + e.getMessage());
view.showMessage( view.showMessage(
LangUtil.translate( LangUtil.translate(
"Failed to export the project to gerber. Please check the log for details"), "Failed to export the project to gerber. Please check the log for details"),
LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE); LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE);
return; return;
} }
@@ -119,30 +121,30 @@ public class GerberExporter {
try { try {
String fileName = layer.formatFileName(fileNameBase, boardComponent.getName()); String fileName = layer.formatFileName(fileNameBase, boardComponent.getName());
LOG.info("Exporting layer: " + layer + " for board " + boardComponent.getName() LOG.info("Exporting layer: " + layer + " for board " + boardComponent.getName()
+ " to file: " + fileName); + " to file: " + fileName);
dataLayer.dumpGerberToFile(fileName); dataLayer.dumpGerberToFile(fileName);
generatedFiles.add(fileName); generatedFiles.add(fileName);
} catch (IOException e) { } catch (IOException e) {
LOG.error("Error writing gerber file: " + e.getMessage()); LOG.error("Error writing gerber file: " + e.getMessage());
view.showMessage( view.showMessage(
LangUtil.translate( LangUtil.translate(
"Failed to export the project to gerber. Please check the log for details"), "Failed to export the project to gerber. Please check the log for details"),
LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE); LangUtil.translate("Gerber Export"), IView.ERROR_MESSAGE);
return; return;
} }
}); });
}); });
view.showMessage( view.showMessage(
String.format( String.format(
LangUtil.translate( LangUtil.translate(
"Gerber export completed successfully. The following files were created:%s"), "Gerber export completed successfully. The following files were created:%s"),
"\n\n" + String.join("\n", generatedFiles)), "\n\n" + String.join("\n", generatedFiles)),
LangUtil.translate("Gerber Export"), IView.INFORMATION_MESSAGE); LangUtil.translate("Gerber Export"), IView.INFORMATION_MESSAGE);
LOG.info("Completed export to gerber"); LOG.info("Completed export to gerber");
} }
private static void drawComponent(Project currentProject, GerberG2DWrapper g2d, private static void drawComponent(Project currentProject, GerberG2DWrapper g2d,
IDIYComponent<?> c, boolean outlineMode) { IDIYComponent<?> c, boolean outlineMode) {
IGerberComponent gerberComponent = (IGerberComponent) c; IGerberComponent gerberComponent = (IGerberComponent) c;
g2d.startedDrawingComponent(); g2d.startedDrawingComponent();
if (gerberComponent instanceof IGerberComponentCustom) { if (gerberComponent instanceof IGerberComponentCustom) {
@@ -151,9 +153,9 @@ public class GerberExporter {
} else if (gerberComponent instanceof IGerberComponentSimple) { } else if (gerberComponent instanceof IGerberComponentSimple) {
IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent; IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent;
g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(), g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(),
gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative()); gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative());
c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d); c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d);
g2d.stopGerberOutput(); g2d.stopGerberOutput();
} }
} }
} }
@@ -1,8 +0,0 @@
package org.diylc.core.gerber;
import java.awt.geom.Rectangle2D;
public interface IGerberBoard {
Rectangle2D getBoardRectangle();
}
@@ -74,7 +74,7 @@ public class DrawingCache {
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) x.getComponent().getClass()); .extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) x.getComponent().getClass());
if (type != null && type.getEnableCache()) { 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 outlineMode
* @param project * @param project
* @param zoom * @param zoom
* @param scaleFactor
* @param zOrder
* @param mirrored
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void draw(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState, 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() ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass()); .extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
@@ -100,7 +104,7 @@ public class DrawingCache {
// if we need to apply caching // if we need to apply caching
Point2D firstPoint = component.getControlPoint(0); 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) { if (value == null) {
return; return;
@@ -180,7 +184,7 @@ public class DrawingCache {
} }
private CacheValue renderAndCache(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState, 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 // if we need to apply caching
Point2D firstPoint = component.getControlPoint(0); 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 // only honor the cache if the component hasn't changed in the meantime
if (value == null || !value.getComponent().equalsTo(component) if (value == null || !value.getComponent().equalsTo(component)
|| value.getState() != componentState || value.getZoom() != zoom || value.getOutlineMode() != outlineMode || 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 // figure out size and placement of buffer image
Rectangle2D rect = component.getCachingBounds(); Rectangle2D rect = component.getCachingBounds();
int width = (int)Math.round(rect.getWidth() * zoom); int width = (int)Math.round(rect.getWidth() * zoom);
@@ -270,7 +274,7 @@ public class DrawingCache {
// add to the cache // add to the cache
value = new CacheValue(component, image, wrapper.getCurrentArea(), wrapper.getContinuityPositiveAreas(), 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<DrawingCache.CacheValue>(value)); imageCache.put(component, new SoftReference<DrawingCache.CacheValue>(value));
} }
@@ -287,6 +291,7 @@ public class DrawingCache {
boolean outlineMode; boolean outlineMode;
double zoom; double zoom;
double scaleFactor; double scaleFactor;
boolean mirrored;
int dx; int dx;
int dy; int dy;
Rectangle2D cacheBounds; Rectangle2D cacheBounds;
@@ -294,7 +299,7 @@ public class DrawingCache {
public CacheValue(IDIYComponent<?> component, BufferedImage image, Area currentArea, public CacheValue(IDIYComponent<?> component, BufferedImage image, Area currentArea,
Map<String, Area> continuityPositiveAreas, Map<String, Area> continuityNegativeAreas, Map<String, Area> continuityPositiveAreas, Map<String, Area> continuityNegativeAreas,
ComponentState state, boolean outlineMode, double zoom, double scaleFactor, int dx, int dy, ComponentState state, boolean outlineMode, double zoom, double scaleFactor, int dx, int dy,
Rectangle2D cacheBounds) { Rectangle2D cacheBounds, boolean mirrored) {
super(); super();
this.cacheBounds = cacheBounds; this.cacheBounds = cacheBounds;
// take a copy of the component, so we can check if it changed in the meantime // 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.state = state;
this.outlineMode = outlineMode; this.outlineMode = outlineMode;
this.zoom = zoom; this.zoom = zoom;
this.scaleFactor = scaleFactor;
this.dx = dx; this.dx = dx;
this.dy = dy; this.dy = dy;
} }
@@ -349,6 +355,10 @@ public class DrawingCache {
return scaleFactor; return scaleFactor;
} }
public boolean isMirrored() {
return mirrored;
}
public int getDx() { public int getDx() {
return dx; return dx;
} }
@@ -1,17 +1,17 @@
/* /*
* *
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors. * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
* *
* This file is part of DIYLC. * This file is part of DIYLC.
* *
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU * 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 * General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version. * 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 * 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 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. * Public License for more details.
* *
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see * You should have received a copy of the GNU General Public License along with DIYLC. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
@@ -46,17 +46,20 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.checkerframework.checker.units.qual.A;
import org.diylc.appframework.miscutils.IConfigurationManager; import org.diylc.appframework.miscutils.IConfigurationManager;
import org.diylc.appframework.simplemq.MessageDispatcher; import org.diylc.appframework.simplemq.MessageDispatcher;
import org.diylc.common.DrawOption; import org.diylc.common.DrawOption;
import org.diylc.common.EventType; import org.diylc.common.EventType;
import org.diylc.common.GridType; import org.diylc.common.GridType;
import org.diylc.common.IComponentFilter; import org.diylc.common.IComponentFilter;
import org.diylc.common.IPlugInPort; import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache; import org.diylc.common.ObjectCache;
import org.diylc.components.AbstractTransparentComponent;
import org.diylc.core.BoardUndersideDisplay;
import org.diylc.core.ComponentForRender; import org.diylc.core.ComponentForRender;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IBoard;
import org.diylc.core.IContinuity; import org.diylc.core.IContinuity;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.ILayeredComponent; 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 * Utility that deals with painting {@link Project} on the {@link Graphics2D} and keeps areas taken
* by each drawn component. * by each drawn component.
* *
* @author Branislav Stojkovic * @author Branislav Stojkovic
*/ */
public class DrawingManager { public class DrawingManager {
@@ -92,6 +95,8 @@ public class DrawingManager {
public static Color CONTROL_POINT_COLOR = Color.blue; public static Color CONTROL_POINT_COLOR = Color.blue;
public static Color SELECTED_CONTROL_POINT_COLOR = Color.green; public static Color SELECTED_CONTROL_POINT_COLOR = Color.green;
private static final Byte MIRRORED_ALPHA = 64;
private Theme theme; private Theme theme;
// Maps keyed by object reference. // Maps keyed by object reference.
@@ -127,7 +132,7 @@ public class DrawingManager {
private List<Area> proximityMarkers = null; private List<Area> proximityMarkers = null;
public DrawingManager(MessageDispatcher<EventType> messageDispatcher, public DrawingManager(MessageDispatcher<EventType> messageDispatcher,
IConfigurationManager<?> configManager) { IConfigurationManager<?> configManager) {
super(); super();
this.messageDispatcher = messageDispatcher; this.messageDispatcher = messageDispatcher;
this.configManager = configManager; this.configManager = configManager;
@@ -145,16 +150,16 @@ public class DrawingManager {
lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>(); lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS); String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS);
debugComponentAreas = debugComponentAreas =
debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true"); debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true");
String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS); String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS);
debugContinuityAreas = 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. * Paints the project onto the canvas and returns the list of components that failed to draw.
* *
* @param g2d * @param g2d
* @param project * @param project
* @param drawOptions * @param drawOptions
@@ -167,16 +172,18 @@ public class DrawingManager {
* @param componentSlot * @param componentSlot
* @param dragInProgress * @param dragInProgress
* @param externalZoom * @param externalZoom
* @param scaleFactor * @param scaleFactor
* @param visibleRect * @param visibleRect
* @return * @return
*/ */
@SuppressWarnings("unlikely-arg-type")
public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project, public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project,
Set<DrawOption> drawOptions, IComponentFilter filter, Rectangle selectionRect, Set<DrawOption> drawOptions, IComponentFilter filter, Rectangle selectionRect,
Collection<IDIYComponent<?>> selectedComponents, Set<IDIYComponent<?>> lockedComponents, Collection<IDIYComponent<?>> selectedComponents, Set<IDIYComponent<?>> lockedComponents,
Set<IDIYComponent<?>> groupedComponents, List<Point2D> controlPointSlot, Set<IDIYComponent<?>> groupedComponents, List<Point2D> controlPointSlot,
List<IDIYComponent<?>> componentSlot, boolean dragInProgress, Double externalZoom, List<IDIYComponent<?>> componentSlot, boolean dragInProgress, Double externalZoom,
Double scaleFactor, Rectangle2D visibleRect) { Double scaleFactor, Rectangle2D visibleRect) {
long totalStartTime = System.nanoTime(); long totalStartTime = System.nanoTime();
failedComponents.clear(); failedComponents.clear();
if (project == null) { if (project == null) {
@@ -195,42 +202,39 @@ public class DrawingManager {
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom); G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
if (drawOptions.contains(DrawOption.ANTIALIASING)) { configureG2D(g2d, drawOptions);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, // group components with boards that contain them
RenderingHints.VALUE_TEXT_ANTIALIAS_ON); Map<IBoard, List<IDIYComponent<?>>> boardMap = project.getComponents().stream()
} else { .filter(IBoard.class::isInstance)
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); .map(IBoard.class::cast)
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, .filter(b -> b.getBoardUndersideDisplay() != BoardUndersideDisplay.NONE)
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); .collect(Collectors.toMap(x -> x, x -> new ArrayList<IDIYComponent<?>>()));
Map<IDIYComponent<?>, List<IBoard>> componentBoardMap = new HashMap<IDIYComponent<?>, List<IBoard>>();
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<IBoard>()).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(); // AffineTransform initialTx = g2d.getTransform();
Dimension canvasDimension = Dimension canvasDimension =
getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE)); getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE));
g2dWrapper.setColor(theme.getBgColor()); g2dWrapper.setColor(theme.getBgColor());
g2dWrapper.fillRect(0, 0, canvasDimension.width, canvasDimension.height); g2dWrapper.fillRect(0, 0, canvasDimension.width, canvasDimension.height);
@@ -243,14 +247,14 @@ public class DrawingManager {
double extraSpace = getExtraSpace(project) * zoom; double extraSpace = getExtraSpace(project) * zoom;
drawGrid(project, g2d, zoom, extraSpace, canvasDimension, visibleRect, drawOptions, drawGrid(project, g2d, zoom, extraSpace, canvasDimension, visibleRect, drawOptions,
extraSpaceRect, extraSpaceTx); extraSpaceRect, extraSpaceTx);
// apply zoom // apply zoom
if (Math.abs(1.0 - zoom) > 1e-4) { if (Math.abs(1.0 - zoom) > 1e-4) {
g2dWrapper.scale(zoom, zoom); g2dWrapper.scale(zoom, zoom);
if (visibleRect != null) if (visibleRect != null)
visibleRect.setRect(visibleRect.getX() / zoom, visibleRect.getY() / zoom, visibleRect.setRect(visibleRect.getX() / zoom, visibleRect.getY() / zoom,
visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom);
} }
// Composite mainComposite = g2d.getComposite(); // Composite mainComposite = g2d.getComposite();
@@ -263,7 +267,7 @@ public class DrawingManager {
Map<IDIYComponent<?>, ComponentState> componentStateMap = new HashMap<IDIYComponent<?>, ComponentState>(); Map<IDIYComponent<?>, ComponentState> componentStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
List<ComponentForRender> componentForRenderList = new ArrayList<ComponentForRender>(); List<ComponentForRender> componentForRenderList = new ArrayList<ComponentForRender>();
for (int i = 0; i < project.getComponents().size(); i++) { 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; ComponentState state = ComponentState.NORMAL;
if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) { if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) {
if (dragInProgress) { if (dragInProgress) {
@@ -287,17 +291,18 @@ public class DrawingManager {
for (int i = 0; i < project.getComponents().size(); i++) { for (int i = 0; i < project.getComponents().size(); i++) {
IDIYComponent<?> component = project.getComponents().get(i); IDIYComponent<?> component = project.getComponents().get(i);
// Do not draw the component if it's filtered out. // Do not draw the component if it's filtered out.
if (filter != null && !filter.testComponent(component)) { if (filter != null && !filter.testComponent(component)) {
continue; continue;
} }
ComponentState state = componentStateMap.get(component); ComponentState state = componentStateMap.get(component);
// Do not track the area if component is not invalidated and was // Do not track the area if component is not invalidated and was
// drawn in the same state. // drawn in the same state.
boolean trackArea = lastDrawnStateMap.get(component) != state; boolean trackArea = lastDrawnStateMap.get(component) != state;
synchronized (g2d) { synchronized (this) {
g2dWrapper.startedDrawingComponent(i); g2dWrapper.startedDrawingComponent(i);
if (!trackArea) { if (!trackArea) {
g2dWrapper.stopTracking(); g2dWrapper.stopTracking();
@@ -312,15 +317,26 @@ public class DrawingManager {
long componentStart = System.nanoTime(); long componentStart = System.nanoTime();
try { try {
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache AffineTransform txBefore = new AffineTransform(g2d.getTransform());
DrawingCache.Instance.draw(component, g2dWrapper, state, drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper,
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect); outlineMode, i, component, state, false);
else // go straight to the wrapper g2dWrapper.setTransform(txBefore);
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
if (g2dWrapper.isTrackingContinuityArea()) { // find one or more boards that contain the component and mirror the component to all
LOG.info("Component " + component.getName() + " of type " List<IBoard> boards = componentBoardMap.get(component);
+ component.getClass().getName() + " did not stop tracking continuity area."); 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) { } catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e); LOG.error("Error drawing " + component.getName(), e);
@@ -362,17 +378,17 @@ public class DrawingManager {
continue; continue;
VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i); VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i);
if ((groupedComponents.contains(component) if ((groupedComponents.contains(component)
&& (visibilityPolicy == VisibilityPolicy.ALWAYS && (visibilityPolicy == VisibilityPolicy.ALWAYS
|| (selectedComponents.contains(component) || (selectedComponents.contains(component)
&& visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED))
|| (!groupedComponents.contains(component) || (!groupedComponents.contains(component)
&& !selectedComponents.contains(component) && !selectedComponents.contains(component)
&& component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) { && component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) {
g2dWrapper.setColor(CONTROL_POINT_COLOR); g2dWrapper.setColor(CONTROL_POINT_COLOR);
Point2D controlPoint = component.getControlPoint(i); Point2D controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE - 2; int pointSize = CONTROL_POINT_SIZE - 2;
g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 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 (IDIYComponent<?> component : selectedComponents) {
for (int i = 0; i < component.getControlPointCount(); i++) { for (int i = 0; i < component.getControlPointCount(); i++) {
if (!groupedComponents.contains(component) if (!groupedComponents.contains(component)
&& (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED
|| component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) { || component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) {
Point2D controlPoint = component.getControlPoint(i); Point2D controlPoint = component.getControlPoint(i);
@@ -389,11 +405,11 @@ public class DrawingManager {
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2), 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.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1), g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1),
(int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, (int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
CONTROL_POINT_SIZE - 2); CONTROL_POINT_SIZE - 2);
} }
} }
} }
@@ -407,7 +423,7 @@ public class DrawingManager {
try { try {
component.draw(g2dWrapper, ComponentState.NORMAL, component.draw(g2dWrapper, ComponentState.NORMAL,
drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e); LOG.error("Error drawing " + component.getName(), e);
@@ -423,12 +439,12 @@ public class DrawingManager {
if (point != null) { if (point != null) {
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2), g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2),
(int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE, (int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE,
CONTROL_POINT_SIZE); CONTROL_POINT_SIZE);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1), g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1),
(int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
CONTROL_POINT_SIZE - 2); CONTROL_POINT_SIZE - 2);
} }
} }
} }
@@ -493,7 +509,7 @@ public class DrawingManager {
// shade extra space // shade extra space
if (drawOptions.contains(DrawOption.EXTRA_SPACE) && SHADE_EXTRA_SPACE && extraSpaceRect != null) { if (drawOptions.contains(DrawOption.EXTRA_SPACE) && SHADE_EXTRA_SPACE && extraSpaceRect != null) {
Area extraSpaceArea = new Area( 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)); extraSpaceArea.subtract(new Area(extraSpaceRect));
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f)); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f));
g2d.setTransform(extraSpaceTx); g2d.setTransform(extraSpaceTx);
@@ -510,6 +526,123 @@ public class DrawingManager {
return failedComponents; return failedComponents;
} }
private void drawMirroredComponent(Project project, Set<DrawOption> 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<DrawOption> 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<DrawOption> 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 double zoomCached = 0;
private Dimension canvasDimensionCached; private Dimension canvasDimensionCached;
private double extraSpaceCached; private double extraSpaceCached;
@@ -517,8 +650,8 @@ public class DrawingManager {
private BufferedImage cachedGridImage; private BufferedImage cachedGridImage;
private void drawGrid(Project project, Graphics2D g2dIn, double zoom, double extraSpace, private void drawGrid(Project project, Graphics2D g2dIn, double zoom, double extraSpace,
Dimension canvasDimension, Rectangle2D visibleRect, Set<DrawOption> drawOptions, Dimension canvasDimension, Rectangle2D visibleRect, Set<DrawOption> drawOptions,
Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) { Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) {
GridType gridType = GridType.LINES; GridType gridType = GridType.LINES;
@@ -526,10 +659,10 @@ public class DrawingManager {
// draw from cache // draw from cache
if (zoom != zoomCached || !canvasDimension.equals(canvasDimensionCached) if (zoom != zoomCached || !canvasDimension.equals(canvasDimensionCached)
|| extraSpace != extraSpaceCached || gridType != gridTypeCached) { || extraSpace != extraSpaceCached || gridType != gridTypeCached) {
try { try {
cachedGridImage = new BufferedImage((int) canvasDimension.getWidth(), cachedGridImage = new BufferedImage((int) canvasDimension.getWidth(),
(int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB); (int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB);
// create graphics // create graphics
Graphics2D g2d = cachedGridImage.createGraphics(); Graphics2D g2d = cachedGridImage.createGraphics();
@@ -538,11 +671,11 @@ public class DrawingManager {
float gridThickness = (float) (1f * (zoom > 1 ? 1 : zoom)); float gridThickness = (float) (1f * (zoom > 1 ? 1 : zoom));
if (gridType == GridType.CROSSHAIR) { if (gridType == GridType.CROSSHAIR) {
g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10f, BasicStroke.JOIN_MITER, 10f,
new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4)); new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4));
} else if (gridType == GridType.DOT) { } else if (gridType == GridType.DOT) {
g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, 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 { } else {
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness)); g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness));
} }
@@ -559,9 +692,9 @@ public class DrawingManager {
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness * 3)); g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness * 3));
g2d.setColor(theme.getDotColor()); g2d.setColor(theme.getDotColor());
for (double i = extraSpace + zoomStep * project.getDotSpacing(); i < extraSpace 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 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); g2d.fillOval((int) Math.round(i - 1), (int) Math.round(j - 1), 3, 3);
} }
} }
@@ -573,12 +706,12 @@ public class DrawingManager {
gridTypeCached = gridType; gridTypeCached = gridType;
} }
} }
if (cachedGridImage == null) { if (cachedGridImage == null) {
LOG.warn("Cached grid image is null!"); LOG.warn("Cached grid image is null!");
return; return;
} }
if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) { if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) {
g2dIn.drawImage(cachedGridImage, 0, 0, null); g2dIn.drawImage(cachedGridImage, 0, 0, null);
} }
@@ -587,10 +720,10 @@ public class DrawingManager {
if (drawOptions.contains(DrawOption.EXTRA_SPACE)) { if (drawOptions.contains(DrawOption.EXTRA_SPACE)) {
float borderThickness = (float) (3f * (zoom > 1 ? 1 : zoom)); float borderThickness = (float) (3f * (zoom > 1 ? 1 : zoom));
g2dIn.setStroke(ObjectCache.getInstance().fetchStroke(borderThickness, 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()); g2dIn.setColor(theme.getOutlineColor());
extraSpaceRect.setRect(new Rectangle2D.Double(extraSpace, extraSpace, extraSpaceRect.setRect(new Rectangle2D.Double(extraSpace, extraSpace,
innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight())); innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight()));
g2dIn.draw(extraSpaceRect); g2dIn.draw(extraSpaceRect);
extraSpaceTx.setTransform(g2dIn.getTransform()); extraSpaceTx.setTransform(g2dIn.getTransform());
@@ -598,7 +731,7 @@ public class DrawingManager {
g2dIn.transform(AffineTransform.getTranslateInstance(extraSpace, extraSpace)); g2dIn.transform(AffineTransform.getTranslateInstance(extraSpace, extraSpace));
if (visibleRect != null) if (visibleRect != null)
visibleRect.setRect(visibleRect.getX() - extraSpace, visibleRect.getY() - extraSpace, 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) { if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) {
lastStatsReportedTime = System.currentTimeMillis(); lastStatsReportedTime = System.currentTimeMillis();
String mapAsString = renderStatsByType.entrySet().stream() String mapAsString = renderStatsByType.entrySet().stream()
.sorted( .sorted(
(e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime()))
.map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}")); .map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}"));
LOG.debug("Render stats: " + mapAsString); LOG.debug("Render stats: " + mapAsString);
LOG.debug("Page stats: " + totalStats.toAvgString()); LOG.debug("Page stats: " + totalStats.toAvgString());
DrawingCache.Instance.logStats(); DrawingCache.Instance.logStats();
@@ -664,11 +797,11 @@ public class DrawingManager {
double height = project.getHeight().convertToPixels(); double height = project.getHeight().convertToPixels();
double targetExtraSpace = EXTRA_SPACE * Math.max(width, height); double targetExtraSpace = EXTRA_SPACE * Math.max(width, height);
return CalcUtils.roundToGrid(targetExtraSpace, return CalcUtils.roundToGrid(targetExtraSpace,
project.getGridSpacing().scale(project.getDotSpacing())); project.getGridSpacing().scale(project.getDotSpacing()));
} }
public Dimension getCanvasDimensions(Project project, Double zoomLevel, public Dimension getCanvasDimensions(Project project, Double zoomLevel,
boolean includeExtraSpace) { boolean includeExtraSpace) {
double width = project.getWidth().convertToPixels(); double width = project.getWidth().convertToPixels();
double height = project.getHeight().convertToPixels(); double height = project.getHeight().convertToPixels();
@@ -705,7 +838,7 @@ public class DrawingManager {
currentContinuityAreas = continuityGraphCache.findAreasFor(p); currentContinuityAreas = continuityGraphCache.findAreasFor(p);
} }
public List<ContinuityArea> getContinuityAreas() { public List<ContinuityArea> getContinuityAreas() {
// Find all individual continuity areas for all components // Find all individual continuity areas for all components
List<ContinuityArea> preliminaryAreas = new ArrayList<ContinuityArea>(); List<ContinuityArea> preliminaryAreas = new ArrayList<ContinuityArea>();
@@ -716,17 +849,17 @@ public class DrawingManager {
if (a == null || a.getOutlineArea() == null) if (a == null || a.getOutlineArea() == null)
continue; continue;
IDIYComponent<?> component = entry.getKey(); IDIYComponent<?> component = entry.getKey();
int layerId; int layerId;
if (component instanceof ILayeredComponent) { if (component instanceof ILayeredComponent) {
layerId = ((ILayeredComponent)component).getLayerId(); layerId = ((ILayeredComponent)component).getLayerId();
} else { } else {
layerId = 0; layerId = 0;
} }
Collection<Area> positiveAreas = a.getContinuityPositiveAreas(); Collection<Area> positiveAreas = a.getContinuityPositiveAreas();
if (positiveAreas != null) { if (positiveAreas != null) {
for (Area a1 : positiveAreas) { for (Area a1 : positiveAreas) {
@@ -734,7 +867,7 @@ public class DrawingManager {
checkBreakout.add(false); checkBreakout.add(false);
} }
} }
Collection<Area> negativeAreas = a.getContinuityNegativeAreas(); Collection<Area> negativeAreas = a.getContinuityNegativeAreas();
if (negativeAreas != null) { if (negativeAreas != null) {
for (Area na : negativeAreas) { for (Area na : negativeAreas) {
@@ -759,9 +892,9 @@ public class DrawingManager {
areas.add(a); areas.add(a);
} else { } else {
areas.addAll(breakoutAreas.stream() areas.addAll(breakoutAreas.stream()
.map(area -> new ContinuityArea(a.getLayerId(), area)) .map(area -> new ContinuityArea(a.getLayerId(), area))
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
} else } else
areas.add(a); areas.add(a);
} }
@@ -781,7 +914,7 @@ public class DrawingManager {
} }
z++; z++;
} }
List<ContinuityArea> areas = getContinuityAreas(); List<ContinuityArea> areas = getContinuityAreas();
return NetlistBuilder.buildContinuityGraph(areas, connections); return NetlistBuilder.buildContinuityGraph(areas, connections);
@@ -790,9 +923,9 @@ public class DrawingManager {
public List<Area> getContinuityAreaProximity(float threshold) { public List<Area> getContinuityAreaProximity(float threshold) {
List<ContinuityArea> continuityAreas = getContinuityAreas(); List<ContinuityArea> continuityAreas = getContinuityAreas();
Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for
// approx good results // approx good results
List<Area> expanded = continuityAreas.parallelStream() List<Area> 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<Area> intersections = new ArrayList<Area>(); List<Area> intersections = new ArrayList<Area>();
for (int i = 0; i < expanded.size() - 1; i++) { for (int i = 0; i < expanded.size() - 1; i++) {
for (int j = i + 1; j < expanded.size(); j++) { for (int j = i + 1; j < expanded.size(); j++) {
@@ -812,13 +945,13 @@ public class DrawingManager {
Rectangle2D bounds2d = a.getBounds2D(); Rectangle2D bounds2d = a.getBounds2D();
if (bounds2d.getWidth() < minSize) if (bounds2d.getWidth() < minSize)
bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(), bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(),
minSize, bounds2d.getHeight()); minSize, bounds2d.getHeight());
if (bounds2d.getHeight() < minSize) if (bounds2d.getHeight() < minSize)
bounds2d = new Rectangle2D.Double(bounds2d.getX(), bounds2d.getCenterY() - minSize / 2, 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(), shapes.add(new Area(new Ellipse2D.Double(bounds2d.getX(), bounds2d.getY(),
bounds2d.getWidth(), bounds2d.getHeight()))); bounds2d.getWidth(), bounds2d.getHeight())));
} }
// AreaUtils.crunchAreas(shapes, null); // AreaUtils.crunchAreas(shapes, null);
@@ -827,4 +960,4 @@ public class DrawingManager {
return shapes; return shapes;
} }
} }
@@ -1,17 +1,17 @@
/* /*
* *
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors. * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
* *
* This file is part of DIYLC. * This file is part of DIYLC.
* *
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU * 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 * General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version. * 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 * 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 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. * Public License for more details.
* *
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see * You should have received a copy of the GNU General Public License along with DIYLC. If not, see
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
@@ -62,7 +62,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.diylc.common.ObjectCache; import org.diylc.common.ObjectCache;
import org.diylc.common.ZoomableStroke; import org.diylc.common.ZoomableStroke;
import org.diylc.core.IDrawingObserver; 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} * {@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, * 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 * retrieved using {@link #finishedDrawingComponent()}. Graphics configuration (color, font, etc) is
* reset between each two components. * reset between each two components.
* *
* @author Branislav Stojkovic * @author Branislav Stojkovic
*/ */
class G2DWrapper extends Graphics2D implements IDrawingObserver { class G2DWrapper extends Graphics2D implements IDrawingObserver {
public static int LINE_SENSITIVITY_MARGIN = 2; 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 drawingComponent = false;
private boolean trackingAllowed = true; private boolean trackingAllowed = true;
private boolean trackingContinuityAllowed = false; private boolean trackingContinuityAllowed = false;
private boolean trackingContinuityPositive = true; private boolean trackingContinuityPositive = true;
private HashMap<String, Integer> trackingStacks = new HashMap<String, Integer>(); private HashMap<String, Integer> trackingStacks = new HashMap<String, Integer>();
private int uniqueCounter = 0; private int uniqueCounter = 0;
private Graphics2D canvasGraphics; private Graphics2D canvasGraphics;
@@ -105,11 +104,13 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
private Shape lastShape; private Shape lastShape;
private double zoom; private double zoom;
private int zOrder; private int zOrder;
private AffineTransform mirrorTransform;
/** /**
* Creates a wrapper around specified {@link Graphics2D} object. * Creates a wrapper around specified {@link Graphics2D} object.
* *
* @param canvasGraphics * @param canvasGraphics
*/ */
public G2DWrapper(Graphics2D canvasGraphics, double zoom) { public G2DWrapper(Graphics2D canvasGraphics, double zoom) {
@@ -122,14 +123,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
continuityMarker = null; continuityMarker = null;
currentTx = new AffineTransform(); currentTx = new AffineTransform();
} }
public Graphics2D getCanvasGraphics() { public Graphics2D getCanvasGraphics() {
return canvasGraphics; return canvasGraphics;
} }
/** /**
* Clears out the current area and caches canvas settings. * Clears out the current area and caches canvas settings.
* *
* @param zOrder * @param zOrder
*/ */
public void startedDrawingComponent(int 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. * Reverts {@link Graphics2D} settings and returns area drawn by component in the meantime.
* *
* @return * @return
*/ */
public ComponentArea finishedDrawingComponent() { public ComponentArea finishedDrawingComponent() {
@@ -163,27 +164,27 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
canvasGraphics.setTransform(originalTx); canvasGraphics.setTransform(originalTx);
canvasGraphics.setComposite(originalComposite); canvasGraphics.setComposite(originalComposite);
canvasGraphics.setFont(originalFont); canvasGraphics.setFont(originalFont);
// Map<String, Integer> sorted = sortByComparator(trackingStacks, false); // Map<String, Integer> sorted = sortByComparator(trackingStacks, false);
return new ComponentArea(zOrder,currentArea, new ArrayList<Area>(continuityPositiveAreas.values()), return new ComponentArea(zOrder,currentArea, new ArrayList<Area>(continuityPositiveAreas.values()),
new ArrayList<Area>(continuityNegativeAreas.values())); new ArrayList<Area>(continuityNegativeAreas.values()));
} }
// start - used for caching // start - used for caching
public Area getCurrentArea() { public Area getCurrentArea() {
return currentArea; return currentArea;
} }
public Map<String, Area> getContinuityPositiveAreas() { public Map<String, Area> getContinuityPositiveAreas() {
return continuityPositiveAreas; return continuityPositiveAreas;
} }
public Map<String, Area> getContinuityNegativeAreas() { public Map<String, Area> getContinuityNegativeAreas() {
return continuityNegativeAreas; return continuityNegativeAreas;
} }
// end - used for caching // end - used for caching
public void merge(Area currentArea, Map<String, Area> continuityPositiveAreas, Map<String, Area> continuityNegativeAreas) { public void merge(Area currentArea, Map<String, Area> continuityPositiveAreas, Map<String, Area> continuityNegativeAreas) {
this.currentArea = currentArea; this.currentArea = currentArea;
this.continuityPositiveAreas = continuityPositiveAreas; this.continuityPositiveAreas = continuityPositiveAreas;
@@ -212,18 +213,22 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
} }
@Override @Override
public boolean isTrackingContinuityArea() { public boolean isTrackingContinuityArea() {
return trackingContinuityAllowed; return trackingContinuityAllowed;
} }
@Override @Override
public void setContinuityMarker(String marker) { 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. * Appends shape interior to the current component area.
* *
* @param s * @param s
*/ */
private void appendShape(Shape s) { private void appendShape(Shape s) {
@@ -237,7 +242,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
area.transform(currentTx); area.transform(currentTx);
if (trackingAllowed) if (trackingAllowed)
currentArea.add(area); currentArea.add(area);
Map<String, Area> toAdd; Map<String, Area> toAdd;
if (trackingContinuityAllowed) { if (trackingContinuityAllowed) {
@@ -250,18 +255,18 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
toAdd.put(marker, area); toAdd.put(marker, area);
// markStack(); // markStack();
} else { } else {
Area current = toAdd.getOrDefault(marker, null); Area current = toAdd.getOrDefault(marker, null);
if (current == null) { if (current == null) {
toAdd.put(marker, area); toAdd.put(marker, area);
// markStack(); // markStack();
} else } else
current.add(area); current.add(area);
} }
} }
lastShape = s; lastShape = s;
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
private void markStack() { private void markStack() {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
@@ -275,7 +280,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
/** /**
* Appends shape outline to the current component area. * Appends shape outline to the current component area.
* *
* @param s * @param s
*/ */
private void appendShapeOutline(Shape s) { private void appendShapeOutline(Shape s) {
@@ -345,22 +350,73 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
@Override @Override
public void drawString(String str, int x, int y) { public void drawString(String str, int x, int y) {
canvasGraphics.drawString(str, x, y); this.drawString(str, (float)x, (float)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 @Override
public void drawString(String str, float x, float y) { 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) { if (drawingComponent && trackingAllowed) {
FontMetrics fontMetrics = canvasGraphics.getFontMetrics(); FontMetrics fontMetrics = canvasGraphics.getFontMetrics();
Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics); Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics);
@@ -569,7 +625,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
canvasGraphics.drawArc(x, y, width, height, startAngle, arcAngle); canvasGraphics.drawArc(x, y, width, height, startAngle, arcAngle);
if (drawingComponent && (trackingAllowed || trackingContinuityAllowed)) { if (drawingComponent && (trackingAllowed || trackingContinuityAllowed)) {
appendShapeOutline(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); appendShapeOutline(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN));
} }
} }
@Override @Override
@@ -599,14 +655,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
@Override @Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, 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 // FIXME: map
return canvasGraphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); return canvasGraphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
} }
@Override @Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, 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 // FIXME: map
return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); 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) { public void setXORMode(Color c1) {
canvasGraphics.setXORMode(c1); canvasGraphics.setXORMode(c1);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap, private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap,
final boolean order) { final boolean order) {
List<Entry<String, Integer>> list = List<Entry<String, Integer>> list =
new LinkedList<Entry<String, Integer>>(unsortMap.entrySet()); new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());
// Sorting the list based on values // Sorting the list based on values
Collections.sort(list, new Comparator<Entry<String, Integer>>() { Collections.sort(list, new Comparator<Entry<String, Integer>>() {
@@ -784,4 +840,4 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
return sortedMap; return sortedMap;
} }
} }
@@ -28,14 +28,15 @@ import java.awt.Graphics2D;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.awt.StringUtils; import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment; import org.diylc.common.HorizontalAlignment;
import org.diylc.common.IPlugInPort; import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache; import org.diylc.common.ObjectCache;
import org.diylc.common.VerticalAlignment; import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.AbstractTransparentComponent;
import org.diylc.core.BoardUndersideDisplay;
import org.diylc.core.ComponentState; import org.diylc.core.ComponentState;
import org.diylc.core.IBoard;
import org.diylc.core.IDrawingObserver; import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project; import org.diylc.core.Project;
import org.diylc.core.VisibilityPolicy; 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.Size;
import org.diylc.core.measures.SizeUnit; import org.diylc.core.measures.SizeUnit;
public abstract class AbstractBoard extends AbstractTransparentComponent<String> { public abstract class AbstractBoard extends AbstractTransparentComponent<String> implements IBoard {
private static final long serialVersionUID = 1L; 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 BORDER_COLOR = PHENOLIC_COLOR.darker();
public static Color COORDINATE_COLOR = Color.gray.brighter(); public static Color COORDINATE_COLOR = Color.gray.brighter();
@@ -56,7 +58,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
protected String value = ""; protected String value = "";
// two control points used for controlling the position // two control points used for controlling the position
protected Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0), 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 // top-left and bottom-right points
protected Point2D.Double firstPoint = new Point2D.Double(); protected Point2D.Double firstPoint = new Point2D.Double();
protected Point2D.Double secondPoint = new Point2D.Double(); protected Point2D.Double secondPoint = new Point2D.Double();
@@ -69,17 +71,20 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
protected CoordinateOrigin coordinateOrigin = CoordinateOrigin.Top_Left; protected CoordinateOrigin coordinateOrigin = CoordinateOrigin.Top_Left;
protected CoordinateDisplay coordinateDisplay = CoordinateDisplay.One_Side; protected CoordinateDisplay coordinateDisplay = CoordinateDisplay.One_Side;
protected CoordinateType yType = CoordinateType.Letters; protected CoordinateType yType = CoordinateType.Letters;
protected Size length; protected Size length;
protected Size width; protected Size width;
protected BoardSizingMode mode = BoardSizingMode.TwoPoints; protected BoardSizingMode mode = BoardSizingMode.TwoPoints;
protected BoardUndersideDisplay boardUndersideDisplay = BoardUndersideDisplay.NONE;
protected Size undersideOffset = DEFAULT_UNDERSIDE_OFFSET;
@Override @Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) { IDrawingObserver drawingObserver) {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
Point2D finalSecondPoint = getFinalSecondPoint(); Point2D finalSecondPoint = getFinalSecondPoint();
Composite oldComposite = g2d.getComposite(); Composite oldComposite = g2d.getComposite();
// render as transparent when dragging // render as transparent when dragging
@@ -90,64 +95,64 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
g2d.setColor(boardColor); g2d.setColor(boardColor);
g2d.fillRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY())); g2d.fillRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
g2d.setComposite(oldComposite); g2d.setComposite(oldComposite);
// Do not track any changes that follow because the whole board has been // Do not track any changes that follow because the whole board has been
// tracked so far. // tracked so far.
drawingObserver.stopTracking(); drawingObserver.stopTracking();
g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR 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())); g2d.drawRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
} }
protected Point2D getFinalSecondPoint() { protected Point2D getFinalSecondPoint() {
Point2D finalSecondPoint; Point2D finalSecondPoint;
if (getMode() == BoardSizingMode.TwoPoints) if (getMode() == BoardSizingMode.TwoPoints)
finalSecondPoint = secondPoint; finalSecondPoint = secondPoint;
else else
finalSecondPoint = new Point2D.Double(firstPoint.getX() + getLength().convertToPixels(), finalSecondPoint = new Point2D.Double(firstPoint.getX() + getLength().convertToPixels(),
firstPoint.getY() + getWidth().convertToPixels()); firstPoint.getY() + getWidth().convertToPixels());
return finalSecondPoint; return finalSecondPoint;
} }
protected void drawCoordinates(Graphics2D g2d, double spacing, Project project) { protected void drawCoordinates(Graphics2D g2d, double spacing, Project project) {
g2d.setColor(coordinateColor); g2d.setColor(coordinateColor);
g2d.setFont(project.getFont().deriveFont(COORDINATE_FONT_SIZE)); g2d.setFont(project.getFont().deriveFont(COORDINATE_FONT_SIZE));
// The half space is used to do rounding when calculating the range. // The half space is used to do rounding when calculating the range.
double halfSpace = spacing / 2; double halfSpace = spacing / 2;
CoordinateOrigin origin = getCoordinateOrigin(); CoordinateOrigin origin = getCoordinateOrigin();
Point2D finalSecondPoint = getFinalSecondPoint(); Point2D finalSecondPoint = getFinalSecondPoint();
if (getCoordinateDisplay() != CoordinateDisplay.None) { if (getCoordinateDisplay() != CoordinateDisplay.None) {
int range; int range;
double yOffset; double yOffset;
CoordinateType yType = getyType(); CoordinateType yType = getyType();
double y = firstPoint.getY(); double y = firstPoint.getY();
if (origin == CoordinateOrigin.Top_Left || origin == CoordinateOrigin.Top_Right) { if (origin == CoordinateOrigin.Top_Left || origin == CoordinateOrigin.Top_Right) {
range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing); range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing);
yOffset = spacing; yOffset = spacing;
} else { } else {
range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing); range = (int) ((finalSecondPoint.getY() - firstPoint.getY() + halfSpace) / spacing);
yOffset = -spacing; yOffset = -spacing;
y = finalSecondPoint.getY(); y = finalSecondPoint.getY();
} }
for (int c = 1; c < range; c++) { for (int c = 1; c < range; c++) {
int xOffset = (yType == CoordinateType.Numbers && c >= 10) || (yType == CoordinateType.Letters && c >= 27) ? 0 : 2; int xOffset = (yType == CoordinateType.Numbers && c >= 10) || (yType == CoordinateType.Letters && c >= 27) ? 0 : 2;
String label = yType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c); String label = yType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c);
y += yOffset; y += yOffset;
StringUtils.drawCenteredText(g2d, label, StringUtils.drawCenteredText(g2d, label,
firstPoint.getX() + xOffset, y, firstPoint.getX() + xOffset, y,
HorizontalAlignment.LEFT, VerticalAlignment.CENTER); HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) { if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) {
StringUtils.drawCenteredText(g2d, label, StringUtils.drawCenteredText(g2d, label,
finalSecondPoint.getX() - xOffset, y, finalSecondPoint.getX() - xOffset, y,
HorizontalAlignment.RIGHT, VerticalAlignment.CENTER); HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
} }
} }
} }
@@ -155,8 +160,8 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
int range; int range;
double xOffset; double xOffset;
CoordinateType xType = getxType(); CoordinateType xType = getxType();
double x = firstPoint.getX(); double x = firstPoint.getX();
if (origin == CoordinateOrigin.Top_Left || origin == CoordinateOrigin.Bottom_Left) { if (origin == CoordinateOrigin.Top_Left || origin == CoordinateOrigin.Bottom_Left) {
range = (int) ((finalSecondPoint.getX() - firstPoint.getX() + halfSpace) / spacing); range = (int) ((finalSecondPoint.getX() - firstPoint.getX() + halfSpace) / spacing);
xOffset = spacing; xOffset = spacing;
@@ -165,19 +170,19 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
xOffset = -spacing; xOffset = -spacing;
x = finalSecondPoint.getX(); x = finalSecondPoint.getX();
} }
for (int c = 1; c < range; c++) { for (int c = 1; c < range; c++) {
String label = xType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c); String label = xType == CoordinateType.Letters ? getCoordinateLabel(c) : Integer.toString(c);
x += xOffset; x += xOffset;
StringUtils.drawCenteredText(g2d, label, StringUtils.drawCenteredText(g2d, label,
x, firstPoint.getY() + 2, x, firstPoint.getY() + 2,
HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) { if (getCoordinateDisplay() == CoordinateDisplay.Both_Sides) {
StringUtils.drawCenteredText(g2d, label, StringUtils.drawCenteredText(g2d, label,
x, (int) (finalSecondPoint.getY() - COORDINATE_FONT_SIZE), x, (int) (finalSecondPoint.getY() - COORDINATE_FONT_SIZE),
HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
} }
} }
} }
@@ -225,7 +230,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
public void setBorderColor(Color borderColor) { public void setBorderColor(Color borderColor) {
this.borderColor = borderColor; this.borderColor = borderColor;
} }
@EditableProperty(name = "X") @EditableProperty(name = "X")
public CoordinateType getxType() { public CoordinateType getxType() {
if (xType == null) if (xType == null)
@@ -250,15 +255,15 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
@EditableProperty(name = "Coordinate Origin") @EditableProperty(name = "Coordinate Origin")
public CoordinateOrigin getCoordinateOrigin() { public CoordinateOrigin getCoordinateOrigin() {
if (coordinateOrigin == null) if (coordinateOrigin == null)
coordinateOrigin = CoordinateOrigin.Top_Left; coordinateOrigin = CoordinateOrigin.Top_Left;
return coordinateOrigin; return coordinateOrigin;
} }
public void setCoordinateOrigin(CoordinateOrigin coordinateOrigin) { public void setCoordinateOrigin(CoordinateOrigin coordinateOrigin) {
this.coordinateOrigin = coordinateOrigin; this.coordinateOrigin = coordinateOrigin;
} }
@EditableProperty(name = "Y") @EditableProperty(name = "Y")
public CoordinateType getyType() { public CoordinateType getyType() {
if (yType == null) if (yType == null)
@@ -294,9 +299,9 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
public void setControlPoint(Point2D point, int index) { public void setControlPoint(Point2D point, int index) {
controlPoints[index].setLocation(point); controlPoints[index].setLocation(point);
firstPoint.setLocation(Math.min(controlPoints[0].getX(), controlPoints[1].getX()), 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()), 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 @EditableProperty
@@ -309,13 +314,13 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
public void setValue(String value) { public void setValue(String value) {
this.value = value; this.value = value;
} }
private SizeUnit getDefaultUnit() { private SizeUnit getDefaultUnit() {
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.METRIC_KEY, true)) if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.METRIC_KEY, true))
return SizeUnit.mm; return SizeUnit.mm;
return SizeUnit.in; return SizeUnit.in;
} }
@EditableProperty(name = "Explicit Length", validatorClass = BoardModeValidator.class) @EditableProperty(name = "Explicit Length", validatorClass = BoardModeValidator.class)
public Size getLength() { public Size getLength() {
if (getMode() == BoardSizingMode.TwoPoints) { if (getMode() == BoardSizingMode.TwoPoints) {
@@ -327,12 +332,12 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
public void setLength(Size length) { public void setLength(Size length) {
if (getMode() == BoardSizingMode.Explicit && length != null && width != null) { if (getMode() == BoardSizingMode.Explicit && length != null && width != null) {
setControlPoint(firstPoint, 0); setControlPoint(firstPoint, 0);
Point2D second = new Point2D.Double(firstPoint.getX() + length.convertToPixels(), Point2D second = new Point2D.Double(firstPoint.getX() + length.convertToPixels(),
firstPoint.getY() + width.convertToPixels()); firstPoint.getY() + width.convertToPixels());
setControlPoint(second, 1); setControlPoint(second, 1);
} }
this.length = length; this.length = length;
} }
@EditableProperty(name = "Explicit Width", validatorClass = BoardModeValidator.class) @EditableProperty(name = "Explicit Width", validatorClass = BoardModeValidator.class)
@@ -352,7 +357,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
} }
this.width = width; this.width = width;
} }
@EditableProperty(name = "Dimension Mode") @EditableProperty(name = "Dimension Mode")
public BoardSizingMode getMode() { public BoardSizingMode getMode() {
if (mode == null) if (mode == null)
@@ -363,27 +368,66 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
public void setMode(BoardSizingMode mode) { public void setMode(BoardSizingMode mode) {
this.mode = 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 @Override
public Rectangle2D getCachingBounds() { public Rectangle2D getCachingBounds() {
Point2D finalSecondPoint = getFinalSecondPoint(); Point2D finalSecondPoint = getFinalSecondPoint();
return new Rectangle2D.Double(Math.min(firstPoint.getX(), finalSecondPoint.getX()) - 2, Math.min(firstPoint.getY(), finalSecondPoint.getY()) - 2, 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); Math.abs(finalSecondPoint.getX() - firstPoint.getX()) + 4, Math.abs(finalSecondPoint.getY() - firstPoint.getY()) + 4);
} }
public static enum CoordinateType { public static enum CoordinateType {
Letters, Numbers Letters, Numbers
} }
public static enum CoordinateDisplay { public static enum CoordinateDisplay {
None, One_Side, Both_Sides; None, One_Side, Both_Sides;
@Override @Override
public String toString() { public String toString() {
return super.toString().replace('_', ' '); return super.toString().replace('_', ' ');
}; };
} }
public static enum CoordinateOrigin { public static enum CoordinateOrigin {
Top_Left, Top_Right, Bottom_Right, Bottom_Left; Top_Left, Top_Right, Bottom_Right, Bottom_Left;
@@ -392,19 +436,19 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
return super.toString().replace('_', ' '); return super.toString().replace('_', ' ');
}; };
} }
public static enum BoardSizingMode { public static enum BoardSizingMode {
TwoPoints("Opposing Points"), Explicit("Explicit Dimensions"); TwoPoints("Opposing Points"), Explicit("Explicit Dimensions");
private String label; private String label;
private BoardSizingMode(String label) { private BoardSizingMode(String label) {
this.label = label; this.label = label;
} }
@Override @Override
public String toString() { public String toString() {
return label; return label;
} }
} }
} }
@@ -28,7 +28,6 @@ import java.awt.Graphics2D;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import org.diylc.common.ObjectCache; import org.diylc.common.ObjectCache;
import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.ComponentState; 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.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.gerber.IGerberBoard;
@ComponentDescriptor(name = "Blank Board", category = "Boards", author = "Branislav Stojkovic", @ComponentDescriptor(name = "Blank Board", category = "Boards", author = "Branislav Stojkovic",
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board",
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, transformer = SimpleComponentTransformer.class) bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, transformer = SimpleComponentTransformer.class)
public class BlankBoard extends AbstractBoard implements IGerberBoard { public class BlankBoard extends AbstractBoard {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -60,14 +58,14 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard {
@Override @Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) { IDrawingObserver drawingObserver) {
Point2D finalSecondPoint = getFinalSecondPoint(); Point2D finalSecondPoint = getFinalSecondPoint();
Shape clip = g2d.getClip(); Shape clip = g2d.getClip();
if (checkPointsClipped(clip) && !clip.contains(firstPoint.getX(), finalSecondPoint.getY()) if (checkPointsClipped(clip) && !clip.contains(firstPoint.getX(), finalSecondPoint.getY())
&& !clip.contains(finalSecondPoint.getX(), firstPoint.getY())) { && !clip.contains(finalSecondPoint.getX(), firstPoint.getY())) {
return; return;
} }
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
if (componentState != ComponentState.DRAGGING) { if (componentState != ComponentState.DRAGGING) {
@@ -86,13 +84,13 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard {
// tracked so far. // tracked so far.
drawingObserver.stopTracking(); drawingObserver.stopTracking();
g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
: borderColor); : borderColor);
if (getType() == Type.SQUARE) if (getType() == Type.SQUARE)
g2d.drawRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY())); g2d.drawRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
else else
g2d.drawOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY())); g2d.drawOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
} }
@Override @Override
public CoordinateType getxType() { public CoordinateType getxType() {
// Override to prevent editing. // Override to prevent editing.
@@ -128,12 +126,17 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard {
public void setType(Type type) { public void setType(Type type) {
this.type = type; this.type = type;
} }
@Override @Override
public String getControlPointNodeName(int index) { public String getControlPointNodeName(int index) {
return null; return null;
} }
@Override
public boolean shouldExportToGerber() {
return true;
}
static enum Type { static enum Type {
ROUND, SQUARE; ROUND, SQUARE;
@@ -142,12 +145,4 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard {
return name().substring(0, 1) + name().substring(1).toLowerCase(); 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());
}
}
+2
View File
@@ -583,6 +583,8 @@ Tubular Beveled
Two Two
Type Type
Ultra-miniature Ultra-miniature
Underside Display
Underside Offset
Underside Pad Color Underside Pad Color
Underside Trace Color Underside Trace Color
Undo Undo