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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user