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,7 +153,7 @@ 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;
} }
@@ -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;
@@ -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,11 +150,11 @@ 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");
} }
/** /**
@@ -171,12 +176,14 @@ public class DrawingManager {
* @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();
@@ -292,12 +296,13 @@ public class DrawingManager {
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);
} }
} }
@@ -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();
@@ -759,8 +892,8 @@ 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);
@@ -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);
@@ -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,7 +69,7 @@ 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.
* *
@@ -107,6 +106,8 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
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.
* *
@@ -165,7 +166,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
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
@@ -221,6 +222,10 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
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.
* *
@@ -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);
@@ -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);
} }
@@ -760,9 +816,9 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
@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>>() {
@@ -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();
@@ -74,9 +76,12 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
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();
@@ -95,7 +100,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
// 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()));
} }
@@ -105,7 +110,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
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;
} }
@@ -126,28 +131,28 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
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);
} }
} }
} }
@@ -172,12 +177,12 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
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);
} }
} }
} }
@@ -250,13 +255,13 @@ 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")
@@ -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
@@ -329,7 +334,7 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
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;
@@ -364,11 +369,50 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
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 {
@@ -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,12 +58,12 @@ 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;
} }
@@ -86,7 +84,7 @@ 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
@@ -134,6 +132,11 @@ public class BlankBoard extends AbstractBoard implements IGerberBoard {
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