mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 02:32:09 +10:00
New parts can be placed in a board's Underside Display; a part placed there goes on the bottom of the board
This commit is contained in:
@@ -39,4 +39,11 @@ public interface IBoardSideComponent {
|
||||
* never an inner layer or null
|
||||
*/
|
||||
PCBLayer getBoardSide();
|
||||
|
||||
/**
|
||||
* Sets the side of the board the component is mounted on. The component is not mirrored.
|
||||
*
|
||||
* @param boardSide {@link PCBLayer#_2} for the top or {@link PCBLayer#_1} for the bottom
|
||||
*/
|
||||
void setBoardSide(PCBLayer boardSide);
|
||||
}
|
||||
|
||||
@@ -118,6 +118,9 @@ public class DrawingManager {
|
||||
|
||||
private List<Area> proximityMarkers = null;
|
||||
|
||||
// the board whose underside mirror the component in the slot is being placed through
|
||||
private IBoard slotMirrorBoard = null;
|
||||
|
||||
public DrawingManager(MessageDispatcher<EventType> messageDispatcher,
|
||||
IConfigurationManager<?> configManager) {
|
||||
super();
|
||||
@@ -418,6 +421,10 @@ public class DrawingManager {
|
||||
}
|
||||
}
|
||||
|
||||
// a component being placed through an underside mirror is shown there, under the cursor
|
||||
AffineTransform slotTx =
|
||||
slotMirrorBoard == null ? null : UndersideMirror.createTransform(slotMirrorBoard);
|
||||
|
||||
// Draw component slot in a separate composite.
|
||||
if (componentSlot != null) {
|
||||
for (IDIYComponent<?> component : componentSlot) {
|
||||
@@ -425,6 +432,10 @@ public class DrawingManager {
|
||||
// leaves behind (e.g. a rotated Label) is reverted before the next one is drawn
|
||||
g2dWrapper.startedDrawingComponent(project.getComponents().size());
|
||||
g2dWrapper.setComposite(slotComposite);
|
||||
if (slotTx != null) {
|
||||
g2dWrapper.transform(slotTx);
|
||||
g2dWrapper.setMirrorDirection(UndersideMirror.getMirrorDirection(slotMirrorBoard));
|
||||
}
|
||||
try {
|
||||
component.draw(g2dWrapper, ComponentState.NORMAL,
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper);
|
||||
@@ -432,6 +443,7 @@ public class DrawingManager {
|
||||
LOG.error("Error drawing " + component.getName(), e);
|
||||
failedComponents.add(component);
|
||||
} finally {
|
||||
g2dWrapper.setMirrorDirection(G2DWrapper.MirrorDirection.NONE);
|
||||
g2dWrapper.finishedDrawingComponent();
|
||||
}
|
||||
}
|
||||
@@ -439,7 +451,9 @@ public class DrawingManager {
|
||||
|
||||
// Draw control points of the component in the slot.
|
||||
if (controlPointSlot != null) {
|
||||
for (Point2D point : controlPointSlot) {
|
||||
for (Point2D slotPoint : controlPointSlot) {
|
||||
Point2D point =
|
||||
slotTx == null || slotPoint == null ? slotPoint : slotTx.transform(slotPoint, null);
|
||||
if (point != null) {
|
||||
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
|
||||
g2dWrapper.fillOval((int) Math.round(point.getX() - CONTROL_POINT_SIZE / 2),
|
||||
@@ -557,20 +571,7 @@ public class DrawingManager {
|
||||
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;
|
||||
}
|
||||
G2DWrapper.MirrorDirection mirrorDirection = UndersideMirror.getMirrorDirection(board);
|
||||
|
||||
AffineTransform oldTx = g2dWrapper.getTransform();
|
||||
Composite oldComposite = g2dWrapper.getComposite();
|
||||
@@ -734,6 +735,16 @@ public class DrawingManager {
|
||||
proximityMarkers = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the board whose underside mirror the component being placed is drawn through, or null to
|
||||
* draw it in the main view.
|
||||
*
|
||||
* @param slotMirrorBoard
|
||||
*/
|
||||
public void setSlotMirrorBoard(IBoard slotMirrorBoard) {
|
||||
this.slotMirrorBoard = slotMirrorBoard;
|
||||
}
|
||||
|
||||
public void clearContinuityArea() {
|
||||
currentContinuityAreas = null;
|
||||
continuityGraphCache = null;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.core.IBoardSideComponent;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
/**
|
||||
* Puts components placed through an Underside Display mirror on the bottom of the board. A part
|
||||
* that is mounted on a side goes to the bottom, copper with a {@link PCBLayer} goes to the bottom
|
||||
* layer, and anything else is left as it is. Nothing is mirrored.
|
||||
*/
|
||||
public class MirrorPlacement {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MirrorPlacement.class);
|
||||
|
||||
private MirrorPlacement() {
|
||||
}
|
||||
|
||||
public static void placeOnBottom(Collection<IDIYComponent<?>> components) {
|
||||
for (IDIYComponent<?> component : components) {
|
||||
if (component instanceof IBoardSideComponent) {
|
||||
((IBoardSideComponent) component).setBoardSide(PCBLayer._1);
|
||||
continue;
|
||||
}
|
||||
// traces, pads and PCB text share no interface for their layer, so it is found the same way
|
||||
// PCBLayerFilter reads it
|
||||
Method setLayer;
|
||||
try {
|
||||
setLayer = component.getClass().getMethod("setLayer", PCBLayer.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
setLayer.invoke(component, PCBLayer._1);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Could not put " + component.getName() + " on the bottom layer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,6 +177,9 @@ public class Presenter implements IPlugInPort {
|
||||
private AffineTransform dragMirrorTx = null;
|
||||
// the reflection of the mirror that the control points in controlPointMap were found through
|
||||
private AffineTransform hoverMirrorTx = null;
|
||||
// the board whose Underside Display mirror the component in the slot is being placed through;
|
||||
// null when it is placed in the main view
|
||||
private IBoard placementMirrorBoard = null;
|
||||
|
||||
private DIYTest test = null;
|
||||
|
||||
@@ -527,6 +530,8 @@ public class Presenter implements IPlugInPort {
|
||||
} else {
|
||||
componentSlotToDraw = instantiationManager.getComponentSlot();
|
||||
}
|
||||
drawingManager.setSlotMirrorBoard(
|
||||
instantiationManager.getComponentTypeSlot() == null ? null : placementMirrorBoard);
|
||||
List<IDIYComponent<?>> failedComponents =
|
||||
drawingManager
|
||||
.drawProject(
|
||||
@@ -626,6 +631,8 @@ public class Presenter implements IPlugInPort {
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
return;
|
||||
}
|
||||
updatePlacementMirror(scaledPoint);
|
||||
scaledPoint = toPlacementSpace(scaledPoint);
|
||||
// Keep the reference to component type for later.
|
||||
ComponentType componentTypeSlot = instantiationManager.getComponentTypeSlot();
|
||||
Template template = instantiationManager.getTemplate();
|
||||
@@ -639,6 +646,9 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
List<IDIYComponent<?>> componentSlot = instantiationManager.getComponentSlot();
|
||||
List<IDIYComponent<?>> newSelection = new ArrayList<IDIYComponent<?>>(componentSlot);
|
||||
if (placementMirrorBoard != null) {
|
||||
MirrorPlacement.placeOnBottom(componentSlot);
|
||||
}
|
||||
addComponents(componentSlot);
|
||||
// group components if there's more than one, e.g. building blocks, but not clipboard
|
||||
// contents
|
||||
@@ -676,6 +686,9 @@ public class Presenter implements IPlugInPort {
|
||||
if (instantiationManager.getComponentSlot() == null) {
|
||||
try {
|
||||
instantiationManager.instatiatePointByPoint(scaledPoint, currentProject);
|
||||
if (placementMirrorBoard != null) {
|
||||
MirrorPlacement.placeOnBottom(instantiationManager.getComponentSlot());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
view.showMessage(ERROR_CREATE, ERROR, IView.ERROR_MESSAGE);
|
||||
LOG.error("Could not create component", e);
|
||||
@@ -1007,10 +1020,14 @@ public class Presenter implements IPlugInPort {
|
||||
Map<IDIYComponent<?>, Set<Integer>> components = new HashMap<IDIYComponent<?>, Set<Integer>>();
|
||||
this.previousScaledPoint = scalePoint(point);
|
||||
if (instantiationManager.getComponentTypeSlot() != null) {
|
||||
IBoard oldPlacementMirrorBoard = placementMirrorBoard;
|
||||
updatePlacementMirror(previousScaledPoint);
|
||||
// reflected before snapping, so that a component placed through a mirror lands on the grid
|
||||
Point2D placementPoint = toPlacementSpace(previousScaledPoint);
|
||||
if (isSnapToGrid()) {
|
||||
CalcUtils.snapPointToGrid(previousScaledPoint, currentProject.getGridSpacing());
|
||||
CalcUtils.snapPointToGrid(placementPoint, currentProject.getGridSpacing());
|
||||
} else if (isSnapToObjects()) {
|
||||
CalcUtils.snapPointToObjects(previousScaledPoint, currentProject.getGridSpacing(), null, currentProject.getComponents());
|
||||
CalcUtils.snapPointToObjects(placementPoint, currentProject.getGridSpacing(), null, currentProject.getComponents());
|
||||
}
|
||||
// What the cursor does is decided by how the slot was armed rather than by the creation
|
||||
// method: a slot instantiated up front carries a whole component that follows the cursor,
|
||||
@@ -1019,13 +1036,13 @@ public class Presenter implements IPlugInPort {
|
||||
if (instantiationManager.getComponentSlot() != null
|
||||
&& instantiationManager.getFirstControlPoint() == null) {
|
||||
refresh =
|
||||
instantiationManager.updateSingleClick(previousScaledPoint, isSnapToGrid(),
|
||||
instantiationManager.updateSingleClick(placementPoint, isSnapToGrid(),
|
||||
currentProject.getGridSpacing());
|
||||
} else {
|
||||
refresh = instantiationManager.updatePointByPoint(
|
||||
constrainToAxis(instantiationManager.getFirstControlPoint(), previousScaledPoint, shiftDown));
|
||||
constrainToAxis(instantiationManager.getFirstControlPoint(), placementPoint, shiftDown));
|
||||
}
|
||||
if (refresh) {
|
||||
if (refresh || placementMirrorBoard != oldPlacementMirrorBoard) {
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
}
|
||||
} else {
|
||||
@@ -3192,6 +3209,22 @@ public class Presenter implements IPlugInPort {
|
||||
: dragMirrorTx.transform(scaledPoint, null);
|
||||
}
|
||||
|
||||
// a component placed point by point stays in the mirror its first point went into
|
||||
private void updatePlacementMirror(Point2D scaledPoint) {
|
||||
if (instantiationManager.getFirstControlPoint() != null) {
|
||||
return;
|
||||
}
|
||||
List<IBoard> boards = UndersideMirror.findBoardsMirroredAt(currentProject, scaledPoint);
|
||||
placementMirrorBoard = boards.isEmpty() ? null : boards.get(0);
|
||||
}
|
||||
|
||||
private Point2D toPlacementSpace(Point2D scaledPoint) {
|
||||
AffineTransform mirrorTx =
|
||||
placementMirrorBoard == null ? null : UndersideMirror.createTransform(placementMirrorBoard);
|
||||
return mirrorTx == null || scaledPoint == null ? scaledPoint
|
||||
: mirrorTx.transform(scaledPoint, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reflection of the first underside mirror that shows a selectable component at the
|
||||
* specified point, or null
|
||||
|
||||
@@ -108,6 +108,26 @@ public class UndersideMirror {
|
||||
return mirrorTx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the axis the board's mirror flips about, which text drawn into it has to be flipped
|
||||
* back along to stay readable
|
||||
*/
|
||||
public static G2DWrapper.MirrorDirection getMirrorDirection(IBoard board) {
|
||||
if (board.getUndersideDisplay() == null) {
|
||||
return G2DWrapper.MirrorDirection.NONE;
|
||||
}
|
||||
switch (board.getUndersideDisplay()) {
|
||||
case ABOVE:
|
||||
case BELOW:
|
||||
return G2DWrapper.MirrorDirection.VERTICAL;
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
return G2DWrapper.MirrorDirection.HORIZONTAL;
|
||||
default:
|
||||
return G2DWrapper.MirrorDirection.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the part of the canvas the board's mirror occupies, or null when the board has no
|
||||
* underside display
|
||||
|
||||
@@ -639,7 +639,7 @@
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Components can be selected, dragged and stretched by clicking them in a board's Underside Display, not only in the main view</description>
|
||||
<description>Components can be selected, dragged and stretched by clicking them in a board's Underside Display, not only in the main view, and new parts can be placed there; a part placed in the Underside Display goes on the bottom of the board</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
|
||||
@@ -173,6 +173,11 @@ public class GerberExporterTest {
|
||||
public PCBLayer getBoardSide() {
|
||||
return side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.side = boardSide;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TextGerberComponent extends SidedGerberComponent {
|
||||
|
||||
@@ -1073,6 +1073,11 @@ public class DrawingManagerTest {
|
||||
return boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
return bounds;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.presenter.DrawingManagerTest.MountedComponent;
|
||||
import org.diylc.testcomponents.MockDIYComponent;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MirrorPlacementTest {
|
||||
|
||||
@Test
|
||||
public void testPlacesOnBottom() {
|
||||
MountedComponent part = new MountedComponent(new Rectangle2D.Double(0, 0, 10, 10), PCBLayer._2);
|
||||
LayeredComponent copper = new LayeredComponent();
|
||||
MockDIYComponent other = new MockDIYComponent();
|
||||
|
||||
MirrorPlacement.placeOnBottom(Arrays.<IDIYComponent<?>>asList(part, copper, other));
|
||||
|
||||
assertEquals(PCBLayer._1, part.getBoardSide());
|
||||
assertEquals(PCBLayer._1, copper.getLayer());
|
||||
}
|
||||
|
||||
public static class LayeredComponent extends MockDIYComponent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private PCBLayer layer = PCBLayer._2;
|
||||
|
||||
public PCBLayer getLayer() {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public void setLayer(PCBLayer layer) {
|
||||
this.layer = layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -379,6 +379,7 @@ public class PCBTerminalBlock extends AbstractTransparentComponent<PCBTerminalBl
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -383,6 +383,7 @@ public class CliffJack1_4 extends AbstractMultiPartComponent<JackType> implement
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -404,6 +404,7 @@ public class CliffJack1_8 extends AbstractMultiPartComponent<JackType> implement
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -390,6 +390,7 @@ public class ClosedJack1_4 extends AbstractMultiPartComponent<JackType> implemen
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -480,6 +480,7 @@ public class MiniRelay extends AbstractLabeledComponent<String> implements IGerb
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -645,6 +645,7 @@ public class MiniToggleSwitch extends AbstractTransparentComponent<ToggleSwitchT
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -304,6 +304,7 @@ public class PlasticDCJack extends AbstractMultiPartComponent<String> implements
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -379,6 +379,7 @@ public class RotarySelectorSwitch extends AbstractMultiPartComponent<String> imp
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -618,6 +618,7 @@ public class RotarySwitchOpen extends AbstractAngledComponent<RotarySwitchOpenTy
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -367,6 +367,7 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -544,6 +544,7 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -526,6 +526,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -155,6 +155,7 @@ public abstract class AbstractFilmCapacitor extends AbstractLeadedComponent<Capa
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -120,6 +120,7 @@ public abstract class AbstractPotentiometer extends AbstractLabeledComponent<Res
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -84,6 +84,7 @@ public abstract class AbstractRadialComponent<T> extends AbstractLeadedComponent
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -535,6 +535,7 @@ public class AudioTransformer extends AbstractMultiPartComponent<String> impleme
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -306,6 +306,7 @@ public class AxialElectrolyticCapacitor extends AbstractLeadedComponent<Capacita
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ public class CrystalOscillator extends AbstractLeadedComponent<Frequency> implem
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -553,6 +553,7 @@ public class HeatSinkResistor extends AbstractLeadedComponent<Resistance>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -551,6 +551,7 @@ public class MultiSectionCapacitor extends AbstractLabeledComponent<Capacitance[
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -272,6 +272,7 @@ public class Resistor extends AbstractLeadedComponent<Resistance>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ public class ToroidalInductor extends AbstractLeadedComponent<Inductance> implem
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -337,6 +337,7 @@ public abstract class AbstractTransistorPackage extends AbstractLabeledComponent
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -717,6 +717,7 @@ public class BridgeRectifier extends AbstractLabeledComponent<String> implements
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -704,6 +704,7 @@ public class DIL_IC extends AbstractLabeledComponent<String>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ public class DiodeGlass extends AbstractLeadedComponent<String>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
+1
@@ -188,6 +188,7 @@ public class DiodePlastic extends AbstractLeadedComponent<String>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -753,6 +753,7 @@ public class MultiwattIC extends AbstractTransparentComponent<String>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -561,6 +561,7 @@ public class SIL_IC extends AbstractTransparentComponent<String>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -422,6 +422,7 @@ public abstract class PassiveSMDComponent<T> extends AbstractLabeledComponent<T>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -493,6 +493,7 @@ public class TubeSocket extends AbstractAngledComponent<String>
|
||||
return boardSide == null ? PCBLayer._2 : boardSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoardSide(PCBLayer boardSide) {
|
||||
this.boardSide = boardSide;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -23,8 +24,10 @@ import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.EventType;
|
||||
import org.diylc.common.IPlugIn;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.boards.BlankBoard;
|
||||
import org.diylc.components.chassis.ChassisPanel;
|
||||
import org.diylc.components.passive.Resistor;
|
||||
import org.diylc.components.composite.CompositeComponent;
|
||||
import org.diylc.core.BoardUndersideDisplay;
|
||||
import org.diylc.core.IBoard;
|
||||
@@ -333,6 +336,48 @@ public class PresenterTests extends TestBase {
|
||||
resistor.getControlPoint(1).getX() - resistor.getControlPoint(0).getX());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceThroughUndersideMirror() {
|
||||
BlankBoard board = mirroredBoard();
|
||||
Rectangle2D region = UndersideMirror.getMirrorRegion(board);
|
||||
Point first = toScreen(new Point2D.Double(region.getMinX() + 100, region.getMinY() + 100));
|
||||
Point second = toScreen(new Point2D.Double(region.getMinX() + 200, region.getMinY() + 100));
|
||||
|
||||
instantiateTwoClick("Passive", "Resistor", first, second);
|
||||
|
||||
Resistor resistor = (Resistor) presenter.getCurrentProject().getComponents().get(1);
|
||||
Assert.assertEquals(PCBLayer._1, resistor.getBoardSide());
|
||||
// created where the mirror shows it, which is on the board itself
|
||||
Assert.assertTrue(board.getBoardRectangle().contains(resistor.getControlPoint(0)));
|
||||
Assert.assertTrue(board.getBoardRectangle().contains(resistor.getControlPoint(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceUnsidedPartThroughUndersideMirror() {
|
||||
BlankBoard board = mirroredBoard();
|
||||
Rectangle2D region = UndersideMirror.getMirrorRegion(board);
|
||||
Point inMirror = toScreen(new Point2D.Double(region.getCenterX(), region.getCenterY()));
|
||||
ComponentType eyeletType = presenter.getComponentTypes().get("Connectivity").stream()
|
||||
.filter(x -> x.getName().equals("Eyelet")).findFirst().get();
|
||||
|
||||
presenter.setNewComponentTypeSlot(eyeletType, null, null, false);
|
||||
presenter.mouseMoved(inMirror, false, false, false);
|
||||
presenter.mouseClicked(inMirror, MouseEvent.BUTTON1, false, false, false, 1);
|
||||
|
||||
IDIYComponent<?> eyelet = presenter.getCurrentProject().getComponents().get(1);
|
||||
Assert.assertTrue(board.getBoardRectangle().contains(eyelet.getControlPoint(0)));
|
||||
}
|
||||
|
||||
// a board with its underside mirror below it, with nothing on it yet
|
||||
private BlankBoard mirroredBoard() {
|
||||
BlankBoard board = new BlankBoard();
|
||||
board.setControlPoint(new Point2D.Double(100, 100), 0);
|
||||
board.setControlPoint(new Point2D.Double(700, 500), 1);
|
||||
board.setUndersideDisplay(BoardUndersideDisplay.BELOW);
|
||||
presenter.getCurrentProject().getComponents().add(board);
|
||||
return board;
|
||||
}
|
||||
|
||||
// a board reaching 200px past the component on every side, with its underside mirror below it;
|
||||
// the component stays selected
|
||||
private IBoard boardUnder(IDIYComponent<?> component) {
|
||||
|
||||
@@ -6,8 +6,9 @@ Gerber layers and the canvas shows it as a part on the far side of the board.
|
||||
Status: 45 parts have a Board Side property, and Gerber export sends a bottom-side part's
|
||||
silkscreen to the bottom layer with its text mirrored. Parts can be selected, dragged and stretched
|
||||
through the Underside Display mirror. The canvas draws a bottom-side part faded in the main view and
|
||||
solid in the mirror. The silkscreen print-out has a separate page for the bottom. The next steps
|
||||
below remain.
|
||||
solid in the mirror. The silkscreen print-out has a separate page for the bottom, and new parts
|
||||
can be placed through the mirror. Every step is done, and the regression suite passes on all 97
|
||||
projects; the open questions below remain.
|
||||
|
||||
## 1. Current state
|
||||
|
||||
@@ -44,6 +45,7 @@ below remain.
|
||||
| The Gerber layer is swapped centrally, not inside each component | Components keep emitting their `*Top` layer. One place maps it to the other side. |
|
||||
| Default `_2` (Top), with `null` meaning Top | Fields added to existing classes deserialize as `null` in old files. |
|
||||
| Bottom-side text is mirrored centrally in the Gerber output | Parts draw their labels readable from the top, which is backwards on the bottom silkscreen once the board is turned over. While `GerberExporter.drawComponent` draws a bottom-side part, `GerberG2DWrapper.setMirrorText` mirrors each string about the centre of its layout box, so no component changes and the canvas keeps readable labels. |
|
||||
| A part without a side placed in the mirror is placed unchanged | A jumper, eyelet or wire has no side to set, so it is placed at the reflected point like any other part rather than refused. |
|
||||
| A `SolderPad` opens the solder mask on its own side | It used to write `SolderMaskBot` whatever its layer, which was wrong for top-layer pads. It now writes `SolderMaskTop` for a pad on `_2`. |
|
||||
|
||||
## 3. Design
|
||||
@@ -118,16 +120,19 @@ order.
|
||||
has nothing. A drag that starts on empty mirror space still draws a selection rectangle in
|
||||
screen space. Moving the board, or changing its Underside Offset, cancels nothing; the mirror
|
||||
simply follows.
|
||||
- **Stage 3: placing in the mirror.** A component instantiated with its first point in a mirror
|
||||
region is created at the reflected point. If it is an `IBoardSideComponent`, it is created with
|
||||
side `_1` (Bottom), not mirrored. A `PCBLayer` component is created on `_1`. Anything else is
|
||||
placed at the reflected point unchanged.
|
||||
- **Stage 3: placing in the mirror (done).** While a component is being placed,
|
||||
`Presenter.placementMirrorBoard` holds the board whose mirror the cursor is in; a single-click
|
||||
part follows the cursor in and out of mirrors, and a part placed point by point stays in the
|
||||
mirror its first point went into. The cursor is reflected before it is snapped to the grid, so
|
||||
the part lands on the grid, and `DrawingManager.setSlotMirrorBoard` draws the part being placed
|
||||
through that mirror, under the cursor. On placement, `MirrorPlacement.placeOnBottom` sets an
|
||||
`IBoardSideComponent` to `_1` (Bottom) and a component with a `PCBLayer` (`setLayer`, found the
|
||||
same way `PCBLayerFilter` reads `getLayer`) to `_1`; nothing is mirrored. Anything else, such as
|
||||
a jumper, eyelet or wire, is placed at the reflected point unchanged. Dragging a new part from
|
||||
the component tree onto a mirror still places it in the main view.
|
||||
|
||||
## 4. Open questions
|
||||
|
||||
- **Components that belong to no side.** In mirror stage 3 a jumper, eyelet or wire dropped in the
|
||||
mirror has no side to set. Confirm that placing it at the reflected point unchanged is what users
|
||||
expect, or whether such parts should be refused in the mirror.
|
||||
- **Locked bottom-side parts drawn from the cache.** An uncached locked part takes the stronger of
|
||||
the locked alpha and the far-side alpha. A cached one is pasted at the locked alpha onto an
|
||||
image that already carries the far-side fade, so it ends up fainter. The same applies to
|
||||
@@ -140,6 +145,10 @@ order.
|
||||
|
||||
- `GerberLayerTest` covers `flip()`. `GerberExporterTest` covers the dispatch for simple components,
|
||||
including a stub `IBoardSideComponent` on top, bottom and `null`.
|
||||
- `MirrorPlacementTest` covers what placing through the mirror changes, and
|
||||
`PresenterTests.testPlaceThroughUndersideMirror` and `testPlaceUnsidedPartThroughUndersideMirror`
|
||||
place a resistor and an eyelet in a mirror and check that they land on the board, the resistor on
|
||||
the bottom.
|
||||
- `SilkScreenFilterTest` covers which side's page each part and board outline goes on, and
|
||||
`SilkScreenDrawingProviderTest` checks that the bottom page appears only for a bottom part.
|
||||
- `DrawingManagerTest` checks rendered pixels for a part that reports its own side: a bottom part
|
||||
@@ -157,9 +166,10 @@ order.
|
||||
- In the app, with Underside Display set to each direction: select, drag and stretch a trace in
|
||||
the mirror and confirm that the original follows correctly. Place a part in the mirror and confirm
|
||||
that it is bottom-side, faded on the main view and solid in the mirror.
|
||||
- Run the regression suite, since rendering and serialization both change.
|
||||
- The regression suite passes on all 97 projects with the whole plan in place.
|
||||
|
||||
## 6. Next steps
|
||||
|
||||
1. Mirror stage 3: placing in the mirror.
|
||||
2. Run the regression suite.
|
||||
1. Settle the open questions above.
|
||||
2. Check Gerber output of a board with parts on both sides in a Gerber viewer, and try the
|
||||
Underside Display editing in each direction in the app, as listed under Verification.
|
||||
|
||||
Reference in New Issue
Block a user