This commit is contained in:
Branislav Stojkovic
2025-03-14 14:06:14 +01:00
parent ccf5d20713
commit 5c745d2d45
5 changed files with 219 additions and 329 deletions
@@ -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(), false); renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder());
} }
}); });
} }
@@ -89,14 +89,10 @@ 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());
@@ -104,7 +100,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, mirrored); CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, scaleFactor, zOrder);
if (value == null) { if (value == null) {
return; return;
@@ -184,7 +180,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 mirrored) { boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder) {
// if we need to apply caching // if we need to apply caching
Point2D firstPoint = component.getControlPoint(0); Point2D firstPoint = component.getControlPoint(0);
@@ -200,7 +196,7 @@ public class DrawingCache {
// only honor the cache if the component hasn't changed in the meantime // 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.isMirrored() != mirrored) { || value.getScaleFactor() != scaleFactor) {
// 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);
@@ -274,7 +270,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, mirrored); wrapper.getContinuityNegativeAreas(), componentState, outlineMode, zoom, scaleFactor, dx, dy, rect);
imageCache.put(component, new SoftReference<DrawingCache.CacheValue>(value)); imageCache.put(component, new SoftReference<DrawingCache.CacheValue>(value));
} }
@@ -291,7 +287,6 @@ 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;
@@ -299,7 +294,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, boolean mirrored) { Rectangle2D cacheBounds) {
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
@@ -314,7 +309,6 @@ 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;
} }
@@ -355,10 +349,6 @@ public class DrawingCache {
return scaleFactor; return scaleFactor;
} }
public boolean isMirrored() {
return mirrored;
}
public int getDx() { public int getDx() {
return dx; return dx;
} }
@@ -46,26 +46,12 @@ 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.EventType; import org.diylc.common.*;
import org.diylc.common.GridType;
import org.diylc.common.IComponentFilter;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.AbstractTransparentComponent;
import org.diylc.core.BoardUndersideDisplay; import org.diylc.core.*;
import org.diylc.core.ComponentForRender;
import org.diylc.core.ComponentState;
import org.diylc.core.IBoard;
import org.diylc.core.IContinuity;
import org.diylc.core.IDIYComponent;
import org.diylc.core.ILayeredComponent;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy;
import org.diylc.core.measures.Size; import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit; import org.diylc.core.measures.SizeUnit;
import org.diylc.netlist.ContinuityGraph; import org.diylc.netlist.ContinuityGraph;
@@ -81,6 +67,7 @@ import org.diylc.utils.Constants;
public class DrawingManager { public class DrawingManager {
private static final Logger LOG = Logger.getLogger(DrawingManager.class); private static final Logger LOG = Logger.getLogger(DrawingManager.class);
public static final byte MIRROR_ALPHA = 64;
public static int CONTROL_POINT_SIZE = 7; public static int CONTROL_POINT_SIZE = 7;
public static double EXTRA_SPACE = 0.25; public static double EXTRA_SPACE = 0.25;
@@ -95,8 +82,6 @@ public class DrawingManager {
public static Color CONTROL_POINT_COLOR = Color.blue; public static Color 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.
@@ -132,7 +117,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;
@@ -150,11 +135,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");
} }
/** /**
@@ -176,14 +161,12 @@ 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) {
@@ -202,7 +185,7 @@ public class DrawingManager {
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom); G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
configureG2D(g2d, drawOptions); configureRenderingHints(g2d, drawOptions);
// group components with boards that contain them // group components with boards that contain them
Map<IBoard, List<IDIYComponent<?>>> boardMap = project.getComponents().stream() Map<IBoard, List<IDIYComponent<?>>> boardMap = project.getComponents().stream()
@@ -210,7 +193,7 @@ public class DrawingManager {
.map(IBoard.class::cast) .map(IBoard.class::cast)
.filter(b -> b.getBoardUndersideDisplay() != BoardUndersideDisplay.NONE) .filter(b -> b.getBoardUndersideDisplay() != BoardUndersideDisplay.NONE)
.collect(Collectors.toMap(x -> x, x -> new ArrayList<IDIYComponent<?>>())); .collect(Collectors.toMap(x -> x, x -> new ArrayList<IDIYComponent<?>>()));
Map<IDIYComponent<?>, List<IBoard>> componentBoardMap = new HashMap<IDIYComponent<?>, List<IBoard>>(); Map<IDIYComponent<?>, Set<IBoard>> componentBoardMap = new HashMap<IDIYComponent<?>, Set<IBoard>>();
for (IBoard board : boardMap.keySet()) { for (IBoard board : boardMap.keySet()) {
Rectangle2D boardRect = board.getBoardRectangle(); Rectangle2D boardRect = board.getBoardRectangle();
@@ -227,14 +210,14 @@ public class DrawingManager {
} }
if (include) { if (include) {
boardMap.get(board).add(c); boardMap.get(board).add(c);
componentBoardMap.computeIfAbsent(c, key -> new ArrayList<IBoard>()).add(board); componentBoardMap.computeIfAbsent(c, key -> new HashSet<IBoard>()).add(board);
} }
} }
} }
// 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);
@@ -247,14 +230,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();
@@ -296,7 +279,6 @@ 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.
@@ -317,26 +299,11 @@ public class DrawingManager {
long componentStart = System.nanoTime(); long componentStart = System.nanoTime();
try { try {
AffineTransform txBefore = new AffineTransform(g2d.getTransform()); drawComponent(project, drawOptions, scaleFactor, visibleRect, component, g2dWrapper, state, zoom, i, outlineMode);
drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper,
outlineMode, i, component, state, false);
g2dWrapper.setTransform(txBefore);
// find one or more boards that contain the component and mirror the component to all if (g2dWrapper.isTrackingContinuityArea()) {
List<IBoard> boards = componentBoardMap.get(component); LOG.info("Component " + component.getName() + " of type "
if (boards != null) { + component.getClass().getName() + " did not stop tracking continuity area.");
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);
@@ -365,6 +332,29 @@ public class DrawingManager {
componentAreaMap.put(component, area); componentAreaMap.put(component, area);
lastDrawnStateMap.put(component, state); lastDrawnStateMap.put(component, state);
} }
// Mirror components that belong to boards that need to be mirrored
if (componentBoardMap.containsKey(component)) {
@SuppressWarnings("unchecked")
ComponentType componentType = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
if (componentType.getTransformer() != null) {
Set<IBoard> boards = componentBoardMap.get(component);
for (IBoard board : boards) {
drawMirroredComponent(project, board, component, componentType, g2dWrapper, state, outlineMode);
}
}
}
// if a component itself is a board that needs to be mirrored, do it
if (IBoard.class.isInstance(component) && componentBoardMap.values().stream()
.anyMatch(boards -> boards.contains(component))) {
ComponentType componentType = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
if (componentType.getTransformer() != null) {
drawMirroredComponent(project, (IBoard) component, component, componentType, g2dWrapper, state, outlineMode);
}
}
} }
} }
@@ -378,17 +368,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);
} }
} }
} }
@@ -397,7 +387,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);
@@ -405,11 +395,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);
} }
} }
} }
@@ -423,7 +413,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);
@@ -439,12 +429,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);
} }
} }
} }
@@ -509,7 +499,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);
@@ -526,121 +516,84 @@ public class DrawingManager {
return failedComponents; return failedComponents;
} }
private void drawMirroredComponent(Project project, Set<DrawOption> drawOptions, private static void drawMirroredComponent(Project project, IBoard board, IDIYComponent<?> component,
Double scaleFactor, Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, ComponentType componentType, G2DWrapper g2dWrapper, ComponentState state, boolean outlineMode) {
boolean outlineMode, int i, ComponentState state, double boardMirrorSpacing, double offset = board.getUndersideOffset().convertToPixels();
IDIYComponent<?> componentToDraw, IBoard board) {
Rectangle2D boardRectangle = board.getBoardRectangle(); 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 { try {
g2dWrapper.setMirrorTransform(mirrorTx); IDIYComponent<?> clonedComponent = component.clone();
g2dWrapper.transform(mirrorTx); int direction = 0;
Point2D pivotPoint = new Point2D.Double();
drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper, outlineMode, i, componentToDraw, state, true); switch (board.getBoardUndersideDisplay()) {
case ABOVE:
// componentToDraw.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); direction = IComponentTransformer.VERTICAL;
} finally { pivotPoint.setLocation(boardRectangle.getMinX(), boardRectangle.getMinY() - offset / 2);
g2dWrapper.setTransform(txBefore); break;
g2dWrapper.setMirrorTransform(null); case BELOW:
if (AbstractTransparentComponent.class.isInstance(componentToDraw)) { direction = IComponentTransformer.VERTICAL;
((AbstractTransparentComponent<?>)componentToDraw).setAlpha(alphaBefore); pivotPoint.setLocation(boardRectangle.getMinX(), boardRectangle.getMaxY() + offset / 2);
break;
case LEFT:
direction = IComponentTransformer.HORIZONTAL;
pivotPoint.setLocation(boardRectangle.getMinX() - offset / 2, boardRectangle.getMinY());
break;
case RIGHT:
direction = IComponentTransformer.HORIZONTAL;
pivotPoint.setLocation(boardRectangle.getMaxX() + offset / 2, boardRectangle.getMinY());
break;
}
componentType.getTransformer().mirror(clonedComponent, pivotPoint, direction);
if (AbstractTransparentComponent.class.isInstance(clonedComponent)) {
AbstractTransparentComponent<?> transparentComponent = (AbstractTransparentComponent<?>) clonedComponent;
transparentComponent.setAlpha(MIRROR_ALPHA);
} }
clonedComponent.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
} }
} }
private void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor, private static void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor, Rectangle2D visibleRect, IDIYComponent<?> component, G2DWrapper g2dWrapper, ComponentState state, double zoom, int i, boolean outlineMode) {
Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, boolean outlineMode, int i, if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
IDIYComponent<?> component, ComponentState state, boolean mirrored) { DrawingCache.Instance.draw(component, g2dWrapper, state,
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect);
AffineTransform txBefore = new AffineTransform(g2dWrapper.getTransform()); else // go straight to the wrapper
try { component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
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) { private void configureRenderingHints(Graphics2D g2d, Set<DrawOption> drawOptions) {
if (drawOptions.contains(DrawOption.ANTIALIASING)) { if (drawOptions.contains(DrawOption.ANTIALIASING)) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON); RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
} else { } else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
} }
if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) { if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY); RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR); RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, // g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
// RenderingHints.VALUE_STROKE_PURE); // RenderingHints.VALUE_STROKE_PURE);
} else { } else {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_SPEED); RenderingHints.VALUE_COLOR_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
} }
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_OFF); RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
} }
private double zoomCached = 0; private double zoomCached = 0;
@@ -650,8 +603,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;
@@ -659,10 +612,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();
@@ -671,11 +624,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));
} }
@@ -692,9 +645,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);
} }
} }
@@ -720,10 +673,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());
@@ -731,7 +684,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());
} }
} }
@@ -740,9 +693,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();
@@ -797,11 +750,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();
@@ -892,8 +845,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);
@@ -923,9 +876,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++) {
@@ -945,13 +898,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,6 +62,7 @@ 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;
@@ -69,7 +70,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(int)} ()} should be called. After the component is drawn, area may be * {@link #startedDrawingComponent()} should be called. After the component is drawn, area may be
* retrieved using {@link #finishedDrawingComponent()}. Graphics configuration (color, font, etc) is * retrieved using {@link #finishedDrawingComponent()}. Graphics configuration (color, font, etc) is
* reset between each two components. * reset between each two components.
* *
@@ -106,8 +107,6 @@ 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.
* *
@@ -166,7 +165,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
@@ -222,10 +221,6 @@ 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.
* *
@@ -350,73 +345,22 @@ 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) {
this.drawString(str, (float)x, (float)y); canvasGraphics.drawString(str, x, y);
if (drawingComponent && trackingAllowed) {
FontMetrics fontMetrics = canvasGraphics.getFontMetrics();
Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics);
Point2D point = new Point2D.Double(x, y);
// currentTx.transform(point, point);
Rectangle2D finalRec =
new Rectangle2D.Double(rect.getX() + point.getX(), rect.getY() + point.getY(), rect.getWidth(),
rect.getHeight());
appendShape(finalRec);
}
} }
@Override @Override
public void drawString(String str, float x, float y) { public void drawString(String str, float x, float y) {
if (this.mirrorTransform == null) { canvasGraphics.drawString(str, x, y);
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);
@@ -655,14 +599,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);
} }
@@ -816,9 +760,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>>() {
@@ -43,7 +43,7 @@ public class CliffJackTransformer implements IComponentTransformer {
@Override @Override
public boolean mirroringChangesCircuit() { public boolean mirroringChangesCircuit() {
return true; return false;
} }
@Override @Override
+3
View File
@@ -70,6 +70,7 @@ And
Angle Angle
Angle Offset Angle Offset
Anti-Aliasing Anti-Aliasing
Apply Model
Arrow Size Arrow Size
Author Author
Auto-Create Pads Auto-Create Pads
@@ -81,6 +82,8 @@ Below
Board Board
Board Color Board Color
Board Width Board Width
Board1
Board2
Boards Boards
Bobbin 1 Bobbin 1
Bobbin 2 Bobbin 2