mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 02:32:09 +10:00
phase 1
This commit is contained in:
@@ -72,5 +72,9 @@ public enum DrawOption {
|
||||
/**
|
||||
* When applied, all colors will be converted to blackest black or whitest white
|
||||
*/
|
||||
MONOCHROMATIC;
|
||||
MONOCHROMATIC,
|
||||
/**
|
||||
* Paint on the bottom side of a board is drawn at reduced opacity, as if seen through the board.
|
||||
*/
|
||||
FADE_FAR_SIDE;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,8 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
public static final String LOCKED_ALPHA = "lockedAlpha";
|
||||
|
||||
public static final String FADE_FAR_SIDE_KEY = "fadeFarSide";
|
||||
|
||||
public static final String LANGUAGE = "language";
|
||||
public static final String LANGUAGE_DEFAULT = "English";
|
||||
|
||||
|
||||
@@ -25,7 +25,11 @@ import org.diylc.core.gerber.GerberLayer;
|
||||
|
||||
public enum PCBLayer {
|
||||
|
||||
_1("Bottom"), _2("Top"), _3("Inner 1"), _4("Inner 2"), _5("Inner 3"), _6("Inner 4"), _7("Inner 5"), _8("Inner 6");
|
||||
_1("Bottom"), _2("Top"),
|
||||
// DIYLC models two-sided boards only. The inner layers are kept, in order, so that files using
|
||||
// them still load with the same layer ids; the editor hides them and Gerber export rejects them.
|
||||
@Deprecated _3("Inner 1"), @Deprecated _4("Inner 2"), @Deprecated _5("Inner 3"),
|
||||
@Deprecated _6("Inner 4"), @Deprecated _7("Inner 5"), @Deprecated _8("Inner 6");
|
||||
|
||||
private String label;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
*/
|
||||
package org.diylc.core;
|
||||
|
||||
import org.diylc.common.PCBLayer;
|
||||
|
||||
/**
|
||||
* Interface for tracking component drawing process. Components may use it to stop or restart
|
||||
* tracking. Anything drawn while tracking is stopped will not be added to the mouse hot-spot area.
|
||||
@@ -65,4 +67,26 @@ public interface IDrawingObserver {
|
||||
* @param marker
|
||||
*/
|
||||
void setContinuityMarker(String marker);
|
||||
|
||||
/**
|
||||
* Notifies the observer that what follows is painted on the given side of the board, until
|
||||
* {@link #stopDrawingSide()}. Paint that is not bracketed is on the top (component) side. The
|
||||
* observer may draw paint on the side facing away from the viewer at reduced opacity, as if seen
|
||||
* through the board. Inner layers count as the bottom side.
|
||||
*
|
||||
* @param side the layer the paint lies on
|
||||
*/
|
||||
default void startDrawingSide(PCBLayer side) {}
|
||||
|
||||
/**
|
||||
* Notifies the observer that what follows is visible from both sides of the board, such as a
|
||||
* drilled hole or a cut through the copper, and is never drawn faded, until
|
||||
* {@link #stopDrawingSide()}.
|
||||
*/
|
||||
default void startDrawingBothSides() {}
|
||||
|
||||
/**
|
||||
* Notifies the observer that paint is back on the top (component) side.
|
||||
*/
|
||||
default void stopDrawingSide() {}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ import org.diylc.utils.Constants;
|
||||
public class DrawingManager {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DrawingManager.class);
|
||||
public static final Percentage MIRROR_ALPHA = new Percentage(50);
|
||||
public static Percentage FAR_SIDE_ALPHA = new Percentage(55);
|
||||
|
||||
public static int CONTROL_POINT_SIZE = 7;
|
||||
public static double EXTRA_SPACE = 0.25;
|
||||
@@ -536,13 +536,58 @@ public class DrawingManager {
|
||||
|
||||
private static void drawMirroredComponent(Project project, IBoard board, IDIYComponent<?> component,
|
||||
G2DWrapper g2dWrapper, ComponentState state, boolean outlineMode) {
|
||||
if (board.getUndersideDisplay() == null || board.getUndersideDisplay() == BoardUndersideDisplay.NONE) {
|
||||
AffineTransform mirrorTx = createMirrorTransform(board);
|
||||
if (mirrorTx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
G2DWrapper.MirrorDirection mirrorDirection;
|
||||
switch (board.getUndersideDisplay()) {
|
||||
case ABOVE:
|
||||
case BELOW:
|
||||
mirrorDirection = G2DWrapper.MirrorDirection.VERTICAL;
|
||||
break;
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
mirrorDirection = G2DWrapper.MirrorDirection.HORIZONTAL;
|
||||
break;
|
||||
default:
|
||||
mirrorDirection = G2DWrapper.MirrorDirection.NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
AffineTransform oldTx = g2dWrapper.getTransform();
|
||||
Composite oldComposite = g2dWrapper.getComposite();
|
||||
g2dWrapper.setMirrorDirection(mirrorDirection);
|
||||
try {
|
||||
g2dWrapper.transform(mirrorTx);
|
||||
// the mirror is the copper side, so copper is drawn solid and the component side faded
|
||||
g2dWrapper.setViewedSide(PCBLayer._1, Boolean.TRUE.equals(board.getUndersideTransparency()),
|
||||
component instanceof IBoard ? PCBLayer._1 : PCBLayer._2);
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error drawing mirrored " + component.getName(), e);
|
||||
} finally {
|
||||
g2dWrapper.setMirrorDirection(G2DWrapper.MirrorDirection.NONE);
|
||||
g2dWrapper.setTransform(oldTx);
|
||||
g2dWrapper.stopDrawingSide();
|
||||
g2dWrapper.resetViewedSide();
|
||||
g2dWrapper.setComposite(oldComposite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reflection that maps the board onto its underside mirror, or null when the board
|
||||
* has no underside display.
|
||||
*/
|
||||
private static AffineTransform createMirrorTransform(IBoard board) {
|
||||
if (board.getUndersideDisplay() == null || board.getUndersideDisplay() == BoardUndersideDisplay.NONE) {
|
||||
return null;
|
||||
}
|
||||
double offset = board.getUndersideOffset() == null ? 0 : board.getUndersideOffset().convertToPixels();
|
||||
Rectangle2D boardRectangle = board.getBoardRectangle();
|
||||
if (boardRectangle == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
double minX = Math.min(boardRectangle.getMinX(), boardRectangle.getMaxX());
|
||||
@@ -581,40 +626,9 @@ public class DrawingManager {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
G2DWrapper.MirrorDirection mirrorDirection;
|
||||
switch (board.getUndersideDisplay()) {
|
||||
case ABOVE:
|
||||
case BELOW:
|
||||
mirrorDirection = G2DWrapper.MirrorDirection.VERTICAL;
|
||||
break;
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
mirrorDirection = G2DWrapper.MirrorDirection.HORIZONTAL;
|
||||
break;
|
||||
default:
|
||||
mirrorDirection = G2DWrapper.MirrorDirection.NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
AffineTransform oldTx = g2dWrapper.getTransform();
|
||||
Composite oldComposite = g2dWrapper.getComposite();
|
||||
g2dWrapper.setMirrorDirection(mirrorDirection);
|
||||
try {
|
||||
g2dWrapper.transform(mirrorTx);
|
||||
if (Boolean.TRUE.equals(board.getUndersideTransparency())) {
|
||||
g2dWrapper.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(MIRROR_ALPHA.getValue().doubleValue() / 100d)));
|
||||
}
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error drawing mirrored " + component.getName(), e);
|
||||
} finally {
|
||||
g2dWrapper.setMirrorDirection(G2DWrapper.MirrorDirection.NONE);
|
||||
g2dWrapper.setTransform(oldTx);
|
||||
g2dWrapper.setComposite(oldComposite);
|
||||
return null;
|
||||
}
|
||||
return mirrorTx;
|
||||
}
|
||||
|
||||
private static void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
@@ -60,6 +61,7 @@ import java.util.Map.Entry;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.common.ZoomableStroke;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
|
||||
@@ -118,6 +120,17 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
private MirrorDirection mirrorDirection = MirrorDirection.NONE;
|
||||
|
||||
private PCBLayer viewedSide = PCBLayer._2;
|
||||
// the side assumed for paint a component does not bracket with startDrawingSide
|
||||
private PCBLayer undeclaredSide = PCBLayer._2;
|
||||
private PCBLayer paintedSide = PCBLayer._2;
|
||||
private boolean fadeFarSide;
|
||||
// the composite the component asked for, held while far-side paint is drawn faded on top of it
|
||||
private Composite unfadedComposite;
|
||||
// boards switch sides once per strip or pad, so the last faded composite is reused
|
||||
private Composite lastFadeInput;
|
||||
private Composite lastFadeOutput;
|
||||
|
||||
private double zoom;
|
||||
private int zOrder;
|
||||
|
||||
@@ -131,6 +144,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
super();
|
||||
this.canvasGraphics = canvasGraphics;
|
||||
this.drawOptions = drawOptions;
|
||||
this.fadeFarSide = drawOptions.contains(DrawOption.FADE_FAR_SIDE);
|
||||
String debugAreaPerformanceStr = System.getProperty(DEBUG_AREA_PERFORMANCE);
|
||||
debugAreaPerformance = "true".equalsIgnoreCase(debugAreaPerformanceStr);
|
||||
this.zoom = zoom;
|
||||
@@ -154,6 +168,31 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
return mirrorDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the side of the board the viewer is looking at and whether paint on the other side is
|
||||
* drawn faded. The main view looks at the top side; the underside mirror looks at the bottom.
|
||||
*
|
||||
* @param viewedSide
|
||||
* @param fadeFarSide
|
||||
* @param undeclaredSide the side assumed for paint that is not bracketed with
|
||||
* {@link #startDrawingSide(PCBLayer)}; a board body is seen from both sides, so the mirror
|
||||
* draws it as the viewed side
|
||||
*/
|
||||
public void setViewedSide(PCBLayer viewedSide, boolean fadeFarSide, PCBLayer undeclaredSide) {
|
||||
this.viewedSide = viewedSide;
|
||||
this.fadeFarSide = fadeFarSide;
|
||||
this.undeclaredSide = undeclaredSide;
|
||||
this.paintedSide = undeclaredSide;
|
||||
updateFarSideFade();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns to the top-side view, fading the bottom side only if the draw options ask for it.
|
||||
*/
|
||||
public void resetViewedSide() {
|
||||
setViewedSide(PCBLayer._2, drawOptions.contains(DrawOption.FADE_FAR_SIDE), PCBLayer._2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the current area and caches canvas settings.
|
||||
*
|
||||
@@ -168,6 +207,8 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
this.debugOutline = new Area();
|
||||
// trackingStacks.clear();
|
||||
this.continuityMarker = null;
|
||||
this.paintedSide = undeclaredSide;
|
||||
updateFarSideFade();
|
||||
this.originalStroke = canvasGraphics.getStroke();
|
||||
this.originalColor = canvasGraphics.getColor();
|
||||
this.originalTx = canvasGraphics.getTransform();
|
||||
@@ -189,6 +230,8 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
public ComponentArea finishedDrawingComponent() {
|
||||
drawingComponent = false;
|
||||
recordingDrawnBounds = false;
|
||||
paintedSide = undeclaredSide;
|
||||
unfadedComposite = null;
|
||||
canvasGraphics.setStroke(originalStroke);
|
||||
canvasGraphics.setColor(originalColor);
|
||||
canvasGraphics.setTransform(originalTx);
|
||||
@@ -280,6 +323,54 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
continuityMarker = marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrawingSide(PCBLayer side) {
|
||||
this.paintedSide = side == null ? undeclaredSide : side;
|
||||
updateFarSideFade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrawingBothSides() {
|
||||
this.paintedSide = viewedSide;
|
||||
updateFarSideFade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopDrawingSide() {
|
||||
this.paintedSide = undeclaredSide;
|
||||
updateFarSideFade();
|
||||
}
|
||||
|
||||
private void updateFarSideFade() {
|
||||
boolean fade = fadeFarSide && !drawOptions.contains(DrawOption.OUTLINE_MODE)
|
||||
&& isBottom(paintedSide) != isBottom(viewedSide);
|
||||
if (fade && unfadedComposite == null) {
|
||||
unfadedComposite = canvasGraphics.getComposite();
|
||||
canvasGraphics.setComposite(fade(unfadedComposite));
|
||||
} else if (!fade && unfadedComposite != null) {
|
||||
canvasGraphics.setComposite(unfadedComposite);
|
||||
unfadedComposite = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isBottom(PCBLayer layer) {
|
||||
return layer != PCBLayer._2;
|
||||
}
|
||||
|
||||
// the stronger of the two fades wins rather than multiplying, so that copper on a locked board,
|
||||
// already drawn at the locked alpha, does not all but disappear
|
||||
private Composite fade(Composite composite) {
|
||||
if (composite != lastFadeInput || lastFadeOutput == null) {
|
||||
float alpha = (float) (DrawingManager.FAR_SIDE_ALPHA.getValue().doubleValue() / 100d);
|
||||
if (composite instanceof AlphaComposite ac && ac.getRule() == AlphaComposite.SRC_OVER) {
|
||||
alpha = Math.min(alpha, ac.getAlpha());
|
||||
}
|
||||
lastFadeInput = composite;
|
||||
lastFadeOutput = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
|
||||
}
|
||||
return lastFadeOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends shape interior to the current component area.
|
||||
*
|
||||
@@ -669,7 +760,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public Composite getComposite() {
|
||||
return canvasGraphics.getComposite();
|
||||
return unfadedComposite != null ? unfadedComposite : canvasGraphics.getComposite();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -738,7 +829,13 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public void setComposite(Composite comp) {
|
||||
if (!drawOptions.contains(DrawOption.OUTLINE_MODE)) {
|
||||
if (drawOptions.contains(DrawOption.OUTLINE_MODE)) {
|
||||
return;
|
||||
}
|
||||
if (unfadedComposite != null) {
|
||||
unfadedComposite = comp;
|
||||
canvasGraphics.setComposite(fade(comp));
|
||||
} else {
|
||||
canvasGraphics.setComposite(comp);
|
||||
}
|
||||
// FIXME: check this.
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.diylc.presenter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
@@ -20,6 +23,7 @@ import org.diylc.appframework.miscutils.InMemoryConfigurationManager;
|
||||
import org.diylc.appframework.simplemq.MessageDispatcher;
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.EventType;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.core.BoardUndersideDisplay;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IBoard;
|
||||
@@ -175,10 +179,10 @@ public class DrawingManagerTest {
|
||||
assertEquals(150.0, mirroredP.getX(), 0.001);
|
||||
assertEquals(670.0, mirroredP.getY(), 0.001);
|
||||
|
||||
// Check transparency
|
||||
// the mirror fades through the wrapper, so the component still sees its own composite
|
||||
assertEquals(2, component.capturedAlphas.size());
|
||||
assertEquals(1.0f, component.capturedAlphas.get(0), 0.01f);
|
||||
assertEquals(0.5f, component.capturedAlphas.get(1), 0.01f);
|
||||
assertEquals(1.0f, component.capturedAlphas.get(1), 0.01f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -213,7 +217,6 @@ public class DrawingManagerTest {
|
||||
assertEquals(150.0, mirroredP.getX(), 0.001);
|
||||
assertEquals(10.0, mirroredP.getY(), 0.001);
|
||||
|
||||
// Transparency should not be 0.5 when undersideTransparency is false
|
||||
assertEquals(1.0f, component.capturedAlphas.get(1), 0.01f);
|
||||
}
|
||||
|
||||
@@ -647,6 +650,177 @@ public class DrawingManagerTest {
|
||||
assertTrue(recorded.contains(50, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBottomSideFadedOnlyWithOption() {
|
||||
Project project = new Project();
|
||||
project.getComponents().add(new SidePaintingComponent(new Rectangle2D.Double(150, 150, 10, 10), null));
|
||||
project.getComponents().add(new SidePaintingComponent(new Rectangle2D.Double(200, 200, 10, 10), PCBLayer._1));
|
||||
project.getComponents().add(new SidePaintingComponent(new Rectangle2D.Double(250, 250, 10, 10), PCBLayer._3));
|
||||
|
||||
BufferedImage faded = renderSides(project, EnumSet.of(DrawOption.ZOOM, DrawOption.FADE_FAR_SIDE));
|
||||
assertSolid(faded, 155, 155);
|
||||
assertFaded(faded, 205, 205);
|
||||
// inner layers count as the bottom side
|
||||
assertFaded(faded, 255, 255);
|
||||
|
||||
BufferedImage plain = renderSides(project, EnumSet.of(DrawOption.ZOOM));
|
||||
assertSolid(plain, 205, 205);
|
||||
assertSolid(plain, 255, 255);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothSidesNeverFaded() {
|
||||
Project project = new Project();
|
||||
MockBoard board = new MockBoard();
|
||||
board.setBoardRectangle(new Rectangle2D.Double(100, 100, 200, 300));
|
||||
board.setUndersideDisplay(BoardUndersideDisplay.BELOW);
|
||||
board.setUndersideOffset(new Size(20.0, SizeUnit.px));
|
||||
project.getComponents().add(board);
|
||||
project.getComponents().add(SidePaintingComponent.bothSides(new Rectangle2D.Double(150, 150, 10, 10)));
|
||||
|
||||
BufferedImage img = renderSides(project, EnumSet.of(DrawOption.ZOOM, DrawOption.FADE_FAR_SIDE));
|
||||
assertSolid(img, 155, 155);
|
||||
assertSolid(img, 155, 665);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLockedBottomSideNotFadedTwice() {
|
||||
Project project = new Project();
|
||||
SidePaintingComponent bottom = new SidePaintingComponent(new Rectangle2D.Double(200, 200, 10, 10), PCBLayer._1);
|
||||
project.getComponents().add(bottom);
|
||||
|
||||
DrawingManager drawingManager = createDrawingManager();
|
||||
BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = img.createGraphics();
|
||||
drawingManager.drawProject(g2d, project,
|
||||
EnumSet.of(DrawOption.ZOOM, DrawOption.FADE_FAR_SIDE, DrawOption.LOCKED_ALPHA), null, null,
|
||||
Collections.emptyList(), Collections.singleton(bottom), Collections.emptySet(), null, null,
|
||||
false, Collections.emptySet(), 1.0, 1.0, null);
|
||||
g2d.dispose();
|
||||
|
||||
// the stronger of the 50% locked alpha and the far-side alpha decides, not their product
|
||||
assertFaded(img, 205, 205, Math.min(0.5f, farSideAlpha()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMirrorShowsCopperSideSolid() {
|
||||
Project project = new Project();
|
||||
MockBoard board = new MockBoard();
|
||||
board.setBoardRectangle(new Rectangle2D.Double(100, 100, 200, 300));
|
||||
board.setUndersideDisplay(BoardUndersideDisplay.BELOW);
|
||||
board.setUndersideOffset(new Size(20.0, SizeUnit.px));
|
||||
board.setUndersideTransparency(true);
|
||||
project.getComponents().add(board);
|
||||
project.getComponents().add(new SidePaintingComponent(new Rectangle2D.Double(150, 150, 10, 10), null));
|
||||
project.getComponents().add(new SidePaintingComponent(new Rectangle2D.Double(200, 200, 10, 10), PCBLayer._1));
|
||||
|
||||
// reflection across y = 410 puts the top-side square at y 660..670 and the bottom one at 610..620
|
||||
BufferedImage img = renderSides(project, EnumSet.of(DrawOption.ZOOM, DrawOption.FADE_FAR_SIDE));
|
||||
assertFaded(img, 155, 665);
|
||||
assertSolid(img, 205, 615);
|
||||
|
||||
board.setUndersideTransparency(false);
|
||||
img = renderSides(project, EnumSet.of(DrawOption.ZOOM, DrawOption.FADE_FAR_SIDE));
|
||||
assertSolid(img, 155, 665);
|
||||
assertSolid(img, 205, 615);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFarSideFadeKeepsComponentComposite() {
|
||||
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D canvas = img.createGraphics();
|
||||
G2DWrapper wrapper = new G2DWrapper(canvas, 1.0, EnumSet.of(DrawOption.FADE_FAR_SIDE));
|
||||
float farSideAlpha = farSideAlpha();
|
||||
|
||||
Composite half = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
|
||||
wrapper.setComposite(half);
|
||||
wrapper.startDrawingSide(PCBLayer._1);
|
||||
assertSame(half, wrapper.getComposite());
|
||||
// the stronger of the two fades wins rather than multiplying
|
||||
assertEquals(Math.min(0.5f, farSideAlpha), ((AlphaComposite) canvas.getComposite()).getAlpha(), 0.001f);
|
||||
|
||||
wrapper.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f));
|
||||
assertEquals(Math.min(0.25f, farSideAlpha), ((AlphaComposite) canvas.getComposite()).getAlpha(), 0.001f);
|
||||
|
||||
wrapper.setComposite(half);
|
||||
wrapper.stopDrawingSide();
|
||||
assertSame(half, canvas.getComposite());
|
||||
canvas.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFarSideNotFadedInOutlineMode() {
|
||||
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D canvas = img.createGraphics();
|
||||
Composite original = canvas.getComposite();
|
||||
G2DWrapper wrapper = new G2DWrapper(canvas, 1.0,
|
||||
EnumSet.of(DrawOption.FADE_FAR_SIDE, DrawOption.OUTLINE_MODE));
|
||||
wrapper.startDrawingSide(PCBLayer._1);
|
||||
assertSame(original, canvas.getComposite());
|
||||
wrapper.stopDrawingSide();
|
||||
canvas.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFarSideFadeLeavesContinuityUnchanged() {
|
||||
Area faded = drawBottomConductor(EnumSet.of(DrawOption.FADE_FAR_SIDE));
|
||||
Area plain = drawBottomConductor(EnumSet.noneOf(DrawOption.class));
|
||||
assertTrue(faded.equals(plain));
|
||||
assertEquals(new Rectangle2D.Double(10, 10, 30, 20), faded.getBounds2D());
|
||||
}
|
||||
|
||||
private Area drawBottomConductor(Set<DrawOption> drawOptions) {
|
||||
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D canvas = img.createGraphics();
|
||||
G2DWrapper wrapper = new G2DWrapper(canvas, 1.0, drawOptions);
|
||||
wrapper.startedDrawingComponent(0);
|
||||
wrapper.startDrawingSide(PCBLayer._1);
|
||||
wrapper.startTrackingContinuityArea(true);
|
||||
wrapper.fill(new Rectangle2D.Double(10, 10, 30, 20));
|
||||
wrapper.stopTrackingContinuityArea();
|
||||
wrapper.stopDrawingSide();
|
||||
ComponentArea componentArea = wrapper.finishedDrawingComponent();
|
||||
canvas.dispose();
|
||||
Area area = new Area();
|
||||
for (Area a : componentArea.getContinuityPositiveAreas()) {
|
||||
area.add(a);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
private BufferedImage renderSides(Project project, Set<DrawOption> drawOptions) {
|
||||
DrawingManager drawingManager = createDrawingManager();
|
||||
BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = img.createGraphics();
|
||||
drawingManager.drawProject(g2d, project, drawOptions, null, null, Collections.emptyList(),
|
||||
Collections.emptySet(), Collections.emptySet(), null, null, false, Collections.emptySet(),
|
||||
1.0, 1.0, null);
|
||||
g2d.dispose();
|
||||
return img;
|
||||
}
|
||||
|
||||
private static float farSideAlpha() {
|
||||
return (float) (DrawingManager.FAR_SIDE_ALPHA.getValue().doubleValue() / 100d);
|
||||
}
|
||||
|
||||
private static int red(BufferedImage img, int x, int y) {
|
||||
return new Color(img.getRGB(x, y)).getRed();
|
||||
}
|
||||
|
||||
// the squares are painted black, so the red channel alone shows how much of the background is left
|
||||
private static void assertSolid(BufferedImage img, int x, int y) {
|
||||
assertEquals(0, red(img, x, y));
|
||||
}
|
||||
|
||||
private static void assertFaded(BufferedImage img, int x, int y) {
|
||||
assertFaded(img, x, y, farSideAlpha());
|
||||
}
|
||||
|
||||
private static void assertFaded(BufferedImage img, int x, int y, float alpha) {
|
||||
double background = red(img, 5, 5);
|
||||
assertEquals(background * (1 - alpha), red(img, x, y), 3);
|
||||
}
|
||||
|
||||
private void drawVisibleRect(DrawingManager drawingManager, Project project,
|
||||
Rectangle2D visibleRect) {
|
||||
BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_ARGB);
|
||||
@@ -732,6 +906,50 @@ public class DrawingManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
// paints an opaque black square, declared on the given side or, when null, left undeclared
|
||||
public static class SidePaintingComponent extends MockDIYComponent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Rectangle2D shape;
|
||||
private PCBLayer side;
|
||||
private boolean bothSides;
|
||||
|
||||
public SidePaintingComponent(Rectangle2D shape, PCBLayer side) {
|
||||
this.shape = shape;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
public static SidePaintingComponent bothSides(Rectangle2D shape) {
|
||||
SidePaintingComponent component = new SidePaintingComponent(shape, null);
|
||||
component.bothSides = true;
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return new Point2D.Double(shape.getCenterX(), shape.getCenterY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
if (bothSides) {
|
||||
drawingObserver.startDrawingBothSides();
|
||||
} else if (side != null) {
|
||||
drawingObserver.startDrawingSide(side);
|
||||
}
|
||||
g2d.setColor(Color.black);
|
||||
g2d.fill(shape);
|
||||
drawingObserver.stopDrawingSide();
|
||||
}
|
||||
}
|
||||
|
||||
// Simple delegating Graphics2D proxy for testing
|
||||
private static class Graphics2DProxy extends Graphics2D {
|
||||
private final Graphics2D delegate;
|
||||
|
||||
Reference in New Issue
Block a user