mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 10:42:09 +10:00
Split trace layer
This commit is contained in:
@@ -364,22 +364,24 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
void setNewComponentTypeSlot(ComponentType componentType, Template template, String[] model, boolean forceInstatiate);
|
||||
|
||||
/**
|
||||
* Locks or unlocks the specified layer. All components within +- 0.5 range will be affected by
|
||||
* the change as well.
|
||||
* Locks or unlocks the specified layer: the components whose z-order rounds to it, or the
|
||||
* components on one side of the board for a trace layer.
|
||||
*
|
||||
* @param layerZOrder
|
||||
* @param layerId a z-order such as {@link org.diylc.core.IDIYComponent#BOARD}, or one of the
|
||||
* trace layers in {@link org.diylc.core.LayerIds}
|
||||
* @param locked
|
||||
*/
|
||||
void setLayerLocked(int layerZOrder, boolean locked);
|
||||
void setLayerLocked(int layerId, boolean locked);
|
||||
|
||||
/**
|
||||
* Shows or hides the specified layer. All components within +- 0.5 range will be affected by the
|
||||
* change as well.
|
||||
* Shows or hides the specified layer: the components whose z-order rounds to it, or the
|
||||
* components on one side of the board for a trace layer.
|
||||
*
|
||||
* @param layerZOrder
|
||||
* @param layerId a z-order such as {@link org.diylc.core.IDIYComponent#BOARD}, or one of the
|
||||
* trace layers in {@link org.diylc.core.LayerIds}
|
||||
* @param visible
|
||||
*/
|
||||
void setLayerVisibility(int layerZOrder, boolean visible);
|
||||
void setLayerVisibility(int layerId, boolean visible);
|
||||
|
||||
/**
|
||||
* @return selection size expressed in both inches or centimeters, respectively
|
||||
|
||||
@@ -77,7 +77,8 @@ public interface ISelectionProcessor extends IGroupProcessor {
|
||||
/**
|
||||
* Selects all components in the project.
|
||||
*
|
||||
* @param int layer if > 0, designates which layer to select. If <= 0 we should select all
|
||||
* @param int layer if > 0, the id of the layer to select, as for
|
||||
* {@link IPlugInPort#setLayerVisibility(int, boolean)}. If <= 0 we should select all
|
||||
* regardless of layer
|
||||
*/
|
||||
void selectAll(int layer);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
|
||||
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.core;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Ids of the layers that can be hidden or locked in a project. A layer is a rounded z-order, such
|
||||
* as {@link IDIYComponent#BOARD}, except that the traces are split into a bottom and a top layer.
|
||||
* <p>
|
||||
* The traces used to be one layer, {@link IDIYComponent#TRACE}, and older DIYLC versions still
|
||||
* know only that one. Project files therefore keep {@link #LEGACY_TRACES} in a hidden or locked
|
||||
* set exactly when both trace layers are in it, so that an older version hides or locks all
|
||||
* traces when the new one has both sides hidden or locked.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public final class LayerIds {
|
||||
|
||||
public static final int LEGACY_TRACES = IDIYComponent.TRACE;
|
||||
public static final int BOTTOM_TRACES = IDIYComponent.TRACE * 10 + 1;
|
||||
public static final int TOP_TRACES = IDIYComponent.TRACE * 10 + 2;
|
||||
|
||||
private LayerIds() {}
|
||||
|
||||
/**
|
||||
* Brings a hidden or locked layer set read from a file up to date. The legacy trace layer, from
|
||||
* an old file or written by an older version, stands for both trace layers. Both trace layers
|
||||
* without it can only mean that an older version showed or unlocked its single trace layer, so
|
||||
* neither side is hidden or locked any more.
|
||||
*/
|
||||
public static void migrateLoaded(Set<Integer> layers) {
|
||||
if (layers.contains(LEGACY_TRACES)) {
|
||||
layers.add(BOTTOM_TRACES);
|
||||
layers.add(TOP_TRACES);
|
||||
} else if (layers.contains(BOTTOM_TRACES) && layers.contains(TOP_TRACES)) {
|
||||
layers.remove(BOTTOM_TRACES);
|
||||
layers.remove(TOP_TRACES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the legacy trace layer in a hidden or locked set after it has been changed: present
|
||||
* exactly when both trace layers are.
|
||||
*/
|
||||
public static void syncLegacy(Set<Integer> layers) {
|
||||
if (layers.contains(BOTTOM_TRACES) && layers.contains(TOP_TRACES)) {
|
||||
layers.add(LEGACY_TRACES);
|
||||
} else {
|
||||
layers.remove(LEGACY_TRACES);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,6 +196,17 @@ public class Project implements Serializable, Cloneable {
|
||||
return hiddenLayers;
|
||||
}
|
||||
|
||||
// called by XStream after loading, whichever way the project is opened; the trace layer was split
|
||||
// in two, and files from older versions name only the single legacy one
|
||||
private Object readResolve() {
|
||||
if (lockedLayers == null) {
|
||||
lockedLayers = new HashSet<Integer>();
|
||||
}
|
||||
LayerIds.migrateLoaded(lockedLayers);
|
||||
LayerIds.migrateLoaded(getHiddenLayers());
|
||||
return this;
|
||||
}
|
||||
|
||||
public VersionNumber getFileVersion() {
|
||||
return fileVersion;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
|
||||
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.util.Set;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.composite.CompositeComponent;
|
||||
import org.diylc.core.IBoardSideComponent;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.ILayeredComponent;
|
||||
import org.diylc.core.LayerIds;
|
||||
|
||||
/**
|
||||
* Decides which layer of the Layers menu a component is on, and with that whether it is hidden or
|
||||
* locked. A component is on the layer of its rounded z-order, except in the trace band, which is
|
||||
* split by board side: copper and other components with a side are on the bottom or top trace
|
||||
* layer, a component without a side is on the top one like the rendering assumes, and a component
|
||||
* that goes through the board, reporting layer id 0, is on both.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public class ComponentLayers {
|
||||
|
||||
// the component is on both trace layers
|
||||
private static final int BOTH_TRACES = -1;
|
||||
// internal components without a descriptor are on no layer
|
||||
private static final int NONE = 0;
|
||||
|
||||
private ComponentLayers() {}
|
||||
|
||||
/**
|
||||
* @return true when every layer the component is on is hidden
|
||||
*/
|
||||
public static boolean isHidden(Set<Integer> hiddenLayers, IDIYComponent<?> component) {
|
||||
int layer = getLayer(component);
|
||||
if (layer == BOTH_TRACES) {
|
||||
return hiddenLayers.contains(LayerIds.BOTTOM_TRACES)
|
||||
&& hiddenLayers.contains(LayerIds.TOP_TRACES);
|
||||
}
|
||||
return layer != NONE && hiddenLayers.contains(layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when any layer the component is on is locked; moving a component that is on both
|
||||
* trace layers changes the routing of both sides
|
||||
*/
|
||||
public static boolean isLocked(Set<Integer> lockedLayers, IDIYComponent<?> component) {
|
||||
int layer = getLayer(component);
|
||||
if (layer == BOTH_TRACES) {
|
||||
return lockedLayers.contains(LayerIds.BOTTOM_TRACES)
|
||||
|| lockedLayers.contains(LayerIds.TOP_TRACES);
|
||||
}
|
||||
return layer != NONE && lockedLayers.contains(layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when the component is on the layer, including a component on both trace layers
|
||||
* when the layer is one of them
|
||||
*/
|
||||
public static boolean isOnLayer(IDIYComponent<?> component, int layerId) {
|
||||
int layer = getLayer(component);
|
||||
if (layer == BOTH_TRACES) {
|
||||
return layerId == LayerIds.BOTTOM_TRACES || layerId == LayerIds.TOP_TRACES;
|
||||
}
|
||||
return layer != NONE && layer == layerId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static int getLayer(IDIYComponent<?> component) {
|
||||
ComponentProcessor processor = ComponentProcessor.getInstance();
|
||||
ComponentType componentType = processor
|
||||
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
|
||||
if (componentType == null) {
|
||||
return NONE;
|
||||
}
|
||||
int layer = (int) Math.round(processor.getZOrder(component));
|
||||
if (layer != IDIYComponent.TRACE) {
|
||||
return layer;
|
||||
}
|
||||
// a building block may hold copper of both sides
|
||||
if (component instanceof CompositeComponent
|
||||
|| (component instanceof ILayeredComponent layered && layered.getLayerId() == 0)) {
|
||||
return BOTH_TRACES;
|
||||
}
|
||||
return IBoardSideComponent.sideOf(component) == PCBLayer._1 ? LayerIds.BOTTOM_TRACES
|
||||
: LayerIds.TOP_TRACES;
|
||||
}
|
||||
}
|
||||
@@ -94,8 +94,7 @@ public class GroupAndLockIndex {
|
||||
if (componentType == null) {
|
||||
return false;
|
||||
}
|
||||
return project.getLockedLayers()
|
||||
.contains((int) Math.round(ComponentProcessor.getInstance().getZOrder(component)))
|
||||
return ComponentLayers.isLocked(project.getLockedLayers(), component)
|
||||
|| project.getLockedComponents().contains(component);
|
||||
}
|
||||
|
||||
|
||||
@@ -488,16 +488,8 @@ public class Presenter implements IPlugInPort {
|
||||
configManager.writeValue(IPlugInPort.DISABLED_CATEGORIES_KEY, new ArrayList<String>());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private boolean isComponentVisible(IDIYComponent<?> component) {
|
||||
ComponentType componentType =
|
||||
ComponentProcessor.getInstance().extractComponentTypeFrom(
|
||||
(Class<? extends IDIYComponent<?>>) component.getClass());
|
||||
// for internal types
|
||||
if (componentType == null)
|
||||
return true;
|
||||
return !currentProject.getHiddenLayers()
|
||||
.contains((int) Math.round(ComponentProcessor.getInstance().getZOrder(component)));
|
||||
return !ComponentLayers.isHidden(currentProject.getHiddenLayers(), component);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1162,7 +1154,7 @@ public class Presenter implements IPlugInPort {
|
||||
Iterator<IDIYComponent<?>> i = newSelection.iterator();
|
||||
while (i.hasNext()) {
|
||||
IDIYComponent<?> c = i.next();
|
||||
if ((int) ComponentProcessor.getInstance().getZOrder(c) != layer) {
|
||||
if (!ComponentLayers.isOnLayer(c, layer)) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
@@ -2285,14 +2277,10 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayerLocked(int layerZOrder, boolean locked) {
|
||||
LOG.info(String.format("setLayerLocked(%s, %s)", layerZOrder, locked));
|
||||
public void setLayerLocked(int layerId, boolean locked) {
|
||||
LOG.info(String.format("setLayerLocked(%s, %s)", layerId, locked));
|
||||
Project oldProject = currentProject.clone();
|
||||
if (locked) {
|
||||
currentProject.getLockedLayers().add(layerZOrder);
|
||||
} else {
|
||||
currentProject.getLockedLayers().remove(layerZOrder);
|
||||
}
|
||||
setLayerInSet(currentProject.getLockedLayers(), layerId, locked);
|
||||
groupAndLockIndex.invalidate();
|
||||
updateSelection(EMPTY_SELECTION);
|
||||
messageDispatcher.dispatchMessage(EventType.LAYER_STATE_CHANGED, currentProject.getLockedLayers());
|
||||
@@ -2301,14 +2289,10 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayerVisibility(int layerZOrder, boolean visible) {
|
||||
LOG.info(String.format("setLayerVisibility(%s, %s)", layerZOrder, visible));
|
||||
public void setLayerVisibility(int layerId, boolean visible) {
|
||||
LOG.info(String.format("setLayerVisibility(%s, %s)", layerId, visible));
|
||||
Project oldProject = currentProject.clone();
|
||||
if (visible) {
|
||||
currentProject.getHiddenLayers().remove(layerZOrder);
|
||||
} else {
|
||||
currentProject.getHiddenLayers().add(layerZOrder);
|
||||
}
|
||||
setLayerInSet(currentProject.getHiddenLayers(), layerId, !visible);
|
||||
updateSelection(EMPTY_SELECTION);
|
||||
messageDispatcher.dispatchMessage(EventType.LAYER_VISIBILITY_CHANGED, currentProject.getHiddenLayers());
|
||||
|
||||
@@ -3225,8 +3209,22 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
|
||||
private boolean isComponentLayerLocked(IDIYComponent<?> component) {
|
||||
return currentProject.getLockedLayers()
|
||||
.contains((int) Math.round(ComponentProcessor.getInstance().getZOrder(component)));
|
||||
return ComponentLayers.isLocked(currentProject.getLockedLayers(), component);
|
||||
}
|
||||
|
||||
// the legacy trace layer stands for both trace layers
|
||||
private static void setLayerInSet(Set<Integer> layers, int layerId, boolean inSet) {
|
||||
int[] ids = layerId == LayerIds.LEGACY_TRACES
|
||||
? new int[] {LayerIds.BOTTOM_TRACES, LayerIds.TOP_TRACES}
|
||||
: new int[] {layerId};
|
||||
for (int id : ids) {
|
||||
if (inSet) {
|
||||
layers.add(id);
|
||||
} else {
|
||||
layers.remove(id);
|
||||
}
|
||||
}
|
||||
LayerIds.syncLegacy(layers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -665,6 +665,10 @@
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Gerber export produces files ready for any PCB fab: drill files are written in Excellon format, which every fab accepts, instead of Gerber format; holes are defined by the drill files alone instead of also being cut out of the copper layers; and solder pads are marked as component pads, or SMD pads when they have no hole, instead of as connector pads</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>The 'Trace' layer in the Layers menu is split into 'Bottom Traces' and 'Top Traces', so the copper of one side can be hidden, locked or selected on its own; vias, eyelets, turrets and plated pads go through the board and belong to both, and jumpers are on the top. The layer shortcuts follow the menu order now, Ctrl+1 to Ctrl+7. Projects with the old 'Trace' layer hidden or locked open with both sides hidden or locked</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Rotated labels in pasted components and building blocks were drawn in the wrong place and at the wrong angle while following the mouse, until the components were placed</description>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
|
||||
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.core;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import com.thoughtworks.xstream.io.xml.DomDriver;
|
||||
|
||||
import org.diylc.serialization.ProjectFileManager;
|
||||
import org.junit.Test;
|
||||
|
||||
public class LayerIdsTest {
|
||||
|
||||
private static final int LEGACY = LayerIds.LEGACY_TRACES;
|
||||
private static final int BOTTOM = LayerIds.BOTTOM_TRACES;
|
||||
private static final int TOP = LayerIds.TOP_TRACES;
|
||||
|
||||
@Test
|
||||
public void testLegacyTraceLayerStandsForBothSides() {
|
||||
assertEquals(set(LEGACY, BOTTOM, TOP, IDIYComponent.BOARD),
|
||||
migrate(LEGACY, IDIYComponent.BOARD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothSidesWithoutLegacyMeansAnOlderVersionClearedTraces() {
|
||||
assertEquals(set(IDIYComponent.BOARD), migrate(BOTTOM, TOP, IDIYComponent.BOARD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneSideIsKept() {
|
||||
assertEquals(set(TOP), migrate(TOP));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyFollowsBothSides() {
|
||||
assertEquals(set(LEGACY, BOTTOM, TOP), sync(BOTTOM, TOP));
|
||||
assertEquals(set(TOP), sync(LEGACY, TOP));
|
||||
assertEquals(set(), sync(LEGACY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadedProjectIsMigrated() {
|
||||
Project project = new Project();
|
||||
project.getHiddenLayers().add(LEGACY);
|
||||
project.getLockedLayers().addAll(set(BOTTOM, TOP));
|
||||
|
||||
XStream xStream = new XStream(new DomDriver("UTF-8"));
|
||||
ProjectFileManager.configure(xStream);
|
||||
Project loaded = (Project) xStream.fromXML(xStream.toXML(project));
|
||||
|
||||
assertEquals(set(LEGACY, BOTTOM, TOP), loaded.getHiddenLayers());
|
||||
assertEquals(set(), loaded.getLockedLayers());
|
||||
}
|
||||
|
||||
private static Set<Integer> migrate(Integer... layers) {
|
||||
Set<Integer> set = set(layers);
|
||||
LayerIds.migrateLoaded(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
private static Set<Integer> sync(Integer... layers) {
|
||||
Set<Integer> set = set(layers);
|
||||
LayerIds.syncLegacy(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
private static Set<Integer> set(Integer... layers) {
|
||||
return new HashSet<Integer>(Arrays.asList(layers));
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IConnective;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.ILayeredComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.AiNote;
|
||||
@@ -52,7 +53,8 @@ import org.diylc.utils.Constants;
|
||||
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class,
|
||||
enableCache = true)
|
||||
public class Eyelet extends AbstractTransparentComponent<String> implements IConnective {
|
||||
public class Eyelet extends AbstractTransparentComponent<String>
|
||||
implements IConnective, ILayeredComponent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -132,6 +134,12 @@ public class Eyelet extends AbstractTransparentComponent<String> implements ICon
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
// goes through the board, so it is on every copper layer
|
||||
@Override
|
||||
public int getLayerId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IConnective;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.ILayeredComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.AiNote;
|
||||
@@ -50,7 +51,8 @@ import org.diylc.utils.Constants;
|
||||
zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class,
|
||||
enableCache = true)
|
||||
public class Turret extends AbstractTransparentComponent<String> implements IConnective {
|
||||
public class Turret extends AbstractTransparentComponent<String>
|
||||
implements IConnective, ILayeredComponent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -127,6 +129,12 @@ public class Turret extends AbstractTransparentComponent<String> implements ICon
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
// goes through the board, so it is on every copper layer
|
||||
@Override
|
||||
public int getLayerId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IConnective;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.ILayeredComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.AiNote;
|
||||
@@ -62,7 +63,7 @@ import org.diylc.utils.Constants;
|
||||
bomPolicy = BomPolicy.NEVER_SHOW, keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB",
|
||||
transformer = SimpleComponentTransformer.class, enableCache = true)
|
||||
public class Via extends AbstractTransparentComponent<Void>
|
||||
implements IConnective, IGerberComponentCustom {
|
||||
implements IConnective, ILayeredComponent, IGerberComponentCustom {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -175,6 +176,12 @@ public class Via extends AbstractTransparentComponent<Void>
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
// goes through the board, so it is on every copper layer
|
||||
@Override
|
||||
public int getLayerId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
|
||||
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.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.composite.CompositeComponent;
|
||||
import org.diylc.components.connectivity.CopperTrace;
|
||||
import org.diylc.components.connectivity.Eyelet;
|
||||
import org.diylc.components.connectivity.Jumper;
|
||||
import org.diylc.components.connectivity.SolderPad;
|
||||
import org.diylc.components.connectivity.Turret;
|
||||
import org.diylc.components.connectivity.Via;
|
||||
import org.diylc.components.passive.Resistor;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.LayerIds;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ComponentLayersTest {
|
||||
|
||||
private static final int BOTTOM = LayerIds.BOTTOM_TRACES;
|
||||
private static final int TOP = LayerIds.TOP_TRACES;
|
||||
|
||||
@Test
|
||||
public void testCopperIsOnTheLayerOfItsSide() {
|
||||
assertOnlyOn(trace(PCBLayer._1), BOTTOM);
|
||||
assertOnlyOn(trace(PCBLayer._2), TOP);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testInnerLayerCopperIsOnTheBottom() {
|
||||
assertOnlyOn(trace(PCBLayer._5), BOTTOM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentsThroughTheBoardAreOnBothTraceLayers() {
|
||||
assertOnBoth(pad(SolderPad.HoleType.PTH));
|
||||
assertOnBoth(new Via());
|
||||
assertOnBoth(new Eyelet());
|
||||
assertOnBoth(new Turret());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonPlatedPadIsOnItsSide() {
|
||||
assertOnlyOn(pad(SolderPad.HoleType.NTPH), BOTTOM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJumperIsOnTopTraces() {
|
||||
assertOnlyOn(new Jumper(), TOP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTraceBlockIsOnBothTraceLayers() {
|
||||
CompositeComponent block = new CompositeComponent();
|
||||
block.getChildComponents().addAll(Arrays.asList(trace(PCBLayer._1), trace(PCBLayer._2)));
|
||||
assertOnBoth(block);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherComponentsKeepTheirZOrderLayer() {
|
||||
Resistor resistor = new Resistor();
|
||||
assertTrue(ComponentLayers.isOnLayer(resistor, IDIYComponent.COMPONENT));
|
||||
assertFalse(ComponentLayers.isOnLayer(resistor, TOP));
|
||||
assertTrue(ComponentLayers.isHidden(set(IDIYComponent.COMPONENT), resistor));
|
||||
assertTrue(ComponentLayers.isLocked(set(IDIYComponent.COMPONENT), resistor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentOnBothIsHiddenWithBothAndLockedWithEither() {
|
||||
Via via = new Via();
|
||||
assertFalse(ComponentLayers.isHidden(set(BOTTOM), via));
|
||||
assertTrue(ComponentLayers.isHidden(set(BOTTOM, TOP), via));
|
||||
assertTrue(ComponentLayers.isLocked(set(BOTTOM), via));
|
||||
assertTrue(ComponentLayers.isLocked(set(TOP), via));
|
||||
assertFalse(ComponentLayers.isLocked(set(IDIYComponent.COMPONENT), via));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJumperStaysUnlockedWithOnlyBottomTracesLocked() {
|
||||
assertFalse(ComponentLayers.isLocked(set(BOTTOM), new Jumper()));
|
||||
}
|
||||
|
||||
private static void assertOnlyOn(IDIYComponent<?> component, int layer) {
|
||||
int other = layer == BOTTOM ? TOP : BOTTOM;
|
||||
assertTrue(ComponentLayers.isOnLayer(component, layer));
|
||||
assertFalse(ComponentLayers.isOnLayer(component, other));
|
||||
assertFalse(ComponentLayers.isOnLayer(component, IDIYComponent.TRACE));
|
||||
assertTrue(ComponentLayers.isHidden(set(layer), component));
|
||||
assertFalse(ComponentLayers.isHidden(set(other), component));
|
||||
assertTrue(ComponentLayers.isLocked(set(layer), component));
|
||||
assertFalse(ComponentLayers.isLocked(set(other), component));
|
||||
}
|
||||
|
||||
private static void assertOnBoth(IDIYComponent<?> component) {
|
||||
assertTrue(ComponentLayers.isOnLayer(component, BOTTOM));
|
||||
assertTrue(ComponentLayers.isOnLayer(component, TOP));
|
||||
}
|
||||
|
||||
private static CopperTrace trace(PCBLayer layer) {
|
||||
CopperTrace trace = new CopperTrace();
|
||||
trace.setLayer(layer);
|
||||
return trace;
|
||||
}
|
||||
|
||||
private static SolderPad pad(SolderPad.HoleType holeType) {
|
||||
SolderPad pad = new SolderPad();
|
||||
pad.setLayer(PCBLayer._1);
|
||||
pad.setHoleType(holeType);
|
||||
return pad;
|
||||
}
|
||||
|
||||
private static Set<Integer> set(Integer... layers) {
|
||||
return new HashSet<Integer>(Arrays.asList(layers));
|
||||
}
|
||||
}
|
||||
+22
-15
@@ -37,6 +37,7 @@ import org.diylc.common.IPlugIn;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IView;
|
||||
import org.diylc.core.LayerIds;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
|
||||
public class LayersMenuPlugin implements IPlugIn {
|
||||
@@ -53,14 +54,14 @@ public class LayersMenuPlugin implements IPlugIn {
|
||||
visibleActionMap = new HashMap<Layer, Action>();
|
||||
selectAllActionMap = new HashMap<Integer, Action>();
|
||||
for (Layer layer : Layer.values()) {
|
||||
final int zOrder = layer.getZOrder();
|
||||
final int layerId = layer.getLayerId();
|
||||
AbstractAction lockAction = new AbstractAction("Lock") {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
LayersMenuPlugin.this.plugInPort.setLayerLocked(zOrder, (Boolean) getValue(Action.SELECTED_KEY));
|
||||
LayersMenuPlugin.this.plugInPort.setLayerLocked(layerId, (Boolean) getValue(Action.SELECTED_KEY));
|
||||
// selectAllActionMap.get(zOrder).setEnabled(!(Boolean) getValue(Action.SELECTED_KEY));
|
||||
}
|
||||
};
|
||||
@@ -73,12 +74,14 @@ public class LayersMenuPlugin implements IPlugIn {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
LayersMenuPlugin.this.plugInPort.setLayerVisibility(zOrder, getValue(Action.SELECTED_KEY) == null ? true : (Boolean) getValue(Action.SELECTED_KEY));
|
||||
LayersMenuPlugin.this.plugInPort.setLayerVisibility(layerId, getValue(Action.SELECTED_KEY) == null ? true : (Boolean) getValue(Action.SELECTED_KEY));
|
||||
// selectAllActionMap.get(zOrder).setEnabled(!(Boolean) getValue(Action.SELECTED_KEY));
|
||||
}
|
||||
};
|
||||
visibleAction.putValue(IView.CHECK_BOX_MENU_ITEM, true);
|
||||
visibleAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_0 + zOrder,
|
||||
// the shortcuts follow the menu order, Ctrl+1 for the first layer
|
||||
visibleAction.putValue(AbstractAction.ACCELERATOR_KEY,
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_1 + layer.ordinal(),
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
|
||||
visibleActionMap.put(layer, visibleAction);
|
||||
|
||||
@@ -88,10 +91,10 @@ public class LayersMenuPlugin implements IPlugIn {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
LayersMenuPlugin.this.plugInPort.selectAll(zOrder);
|
||||
LayersMenuPlugin.this.plugInPort.selectAll(layerId);
|
||||
}
|
||||
};
|
||||
selectAllActionMap.put(zOrder, selectAllAction);
|
||||
selectAllActionMap.put(layerId, selectAllAction);
|
||||
|
||||
swingUI.injectSubmenu(layer.title, null, LOCK_LAYERS_TITLE);
|
||||
swingUI.injectMenuAction(visibleAction, layer.title);
|
||||
@@ -116,34 +119,38 @@ public class LayersMenuPlugin implements IPlugIn {
|
||||
if (eventType == EventType.LAYER_STATE_CHANGED) {
|
||||
Set<Integer> lockedLayers = (Set<Integer>) params[0];
|
||||
for (Layer layer : Layer.values()) {
|
||||
lockActionMap.get(layer).putValue(Action.SELECTED_KEY, lockedLayers.contains(layer.getZOrder()));
|
||||
lockActionMap.get(layer).putValue(Action.SELECTED_KEY, lockedLayers.contains(layer.getLayerId()));
|
||||
}
|
||||
} else if (eventType == EventType.LAYER_VISIBILITY_CHANGED) {
|
||||
Set<Integer> hiddenLayers = (Set<Integer>) params[0];
|
||||
for (Layer layer : Layer.values()) {
|
||||
visibleActionMap.get(layer).putValue(Action.SELECTED_KEY, !hiddenLayers.contains(layer.getZOrder()));
|
||||
visibleActionMap.get(layer).putValue(Action.SELECTED_KEY, !hiddenLayers.contains(layer.getLayerId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in menu order, which also gives the shortcuts
|
||||
static enum Layer {
|
||||
CHASSIS("Chassis", IDIYComponent.CHASSIS), BOARD("Board", IDIYComponent.BOARD), TRACE("Trace", IDIYComponent.TRACE), COMPONENT(
|
||||
"Component", IDIYComponent.COMPONENT), WIRING("Wiring", IDIYComponent.WIRING), TEXT("Text", IDIYComponent.TEXT);
|
||||
CHASSIS("Chassis", IDIYComponent.CHASSIS), BOARD("Board", IDIYComponent.BOARD),
|
||||
BOTTOM_TRACES("Bottom Traces", LayerIds.BOTTOM_TRACES),
|
||||
TOP_TRACES("Top Traces", LayerIds.TOP_TRACES),
|
||||
COMPONENT("Component", IDIYComponent.COMPONENT), WIRING("Wiring", IDIYComponent.WIRING),
|
||||
TEXT("Text", IDIYComponent.TEXT);
|
||||
|
||||
String title;
|
||||
int zOrder;
|
||||
int layerId;
|
||||
|
||||
private Layer(String title, int order) {
|
||||
private Layer(String title, int layerId) {
|
||||
this.title = title;
|
||||
zOrder = order;
|
||||
this.layerId = layerId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public int getZOrder() {
|
||||
return zOrder;
|
||||
public int getLayerId() {
|
||||
return layerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,3 +734,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Dezimal (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Kupfer unter der Platine abblenden
|
||||
Board Side|Platinenseite
|
||||
Bottom Traces|Leiterbahnen unten
|
||||
Top Traces|Leiterbahnen oben
|
||||
|
||||
@@ -727,3 +727,5 @@ Decimal (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board
|
||||
Board Side
|
||||
Bottom Traces
|
||||
Top Traces
|
||||
|
||||
@@ -730,3 +730,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Decimal (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Atenuar cobre bajo la placa
|
||||
Board Side|Lado de la placa
|
||||
Bottom Traces|Trazos inferiores
|
||||
Top Traces|Trazos superiores
|
||||
|
||||
@@ -729,3 +729,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Décimale (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Estomper le cuivre sous la carte
|
||||
Board Side|Côté de la carte
|
||||
Bottom Traces|Pistes du dessous
|
||||
Top Traces|Pistes du dessus
|
||||
|
||||
@@ -731,3 +731,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Decimale (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Attenua il rame sotto la scheda
|
||||
Board Side|Lato della scheda
|
||||
Bottom Traces|Tracce inferiori
|
||||
Top Traces|Tracce superiori
|
||||
|
||||
@@ -735,3 +735,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Decimaal (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Koper onder de print vervagen
|
||||
Board Side|Printzijde
|
||||
Bottom Traces|Sporen onderkant
|
||||
Top Traces|Sporen bovenkant
|
||||
|
||||
@@ -730,3 +730,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Decimal (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Esmaecer cobre sob a placa
|
||||
Board Side|Lado da placa
|
||||
Bottom Traces|Traçados inferiores
|
||||
Top Traces|Traçados superiores
|
||||
|
||||
@@ -726,3 +726,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|Decimalni (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|Prigušen Bakar Ispod Ploče
|
||||
Board Side|Strana Ploče
|
||||
Bottom Traces|Donji Vodovi
|
||||
Top Traces|Gornji Vodovi
|
||||
|
||||
@@ -728,3 +728,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|दशमलव (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|बोर्ड के नीचे तांबा धुंधला करें
|
||||
Board Side|बोर्ड का पक्ष
|
||||
Bottom Traces|नीचे के ट्रेस
|
||||
Top Traces|ऊपर के ट्रेस
|
||||
|
||||
@@ -728,3 +728,5 @@ Decimal (4.7K, 0.22uF, 6.3V)|小数 (4.7K, 0.22uF, 6.3V)
|
||||
RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3)
|
||||
Fade Copper Under Board|淡化板下铜箔
|
||||
Board Side|板面
|
||||
Bottom Traces|底层走线
|
||||
Top Traces|顶层走线
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.common.PropertyWrapper;
|
||||
import org.diylc.components.connectivity.CopperTrace;
|
||||
import org.diylc.components.connectivity.Jumper;
|
||||
import org.diylc.components.connectivity.Via;
|
||||
import org.diylc.core.LayerIds;
|
||||
import org.diylc.core.Project;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TraceLayerTests extends TestBase {
|
||||
|
||||
private CopperTrace bottomTrace;
|
||||
private CopperTrace topTrace;
|
||||
private Via via;
|
||||
private Jumper jumper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
bottomTrace = trace(PCBLayer._1);
|
||||
topTrace = trace(PCBLayer._2);
|
||||
via = new Via();
|
||||
jumper = new Jumper();
|
||||
presenter.getCurrentProject().getComponents()
|
||||
.addAll(Arrays.asList(bottomTrace, topTrace, via, jumper));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectAllOfATraceLayer() {
|
||||
presenter.selectAll(LayerIds.BOTTOM_TRACES);
|
||||
assertEquals(new HashSet<Object>(Arrays.asList(bottomTrace, via)),
|
||||
new HashSet<Object>(presenter.getSelectedComponents()));
|
||||
|
||||
presenter.selectAll(LayerIds.TOP_TRACES);
|
||||
assertEquals(new HashSet<Object>(Arrays.asList(topTrace, via, jumper)),
|
||||
new HashSet<Object>(presenter.getSelectedComponents()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLockingBottomTracesLocksTheViaButNotTheTopSide() {
|
||||
presenter.setLayerLocked(LayerIds.BOTTOM_TRACES, true);
|
||||
|
||||
presenter.selectAll(0);
|
||||
assertEquals(new HashSet<Object>(Arrays.asList(topTrace, jumper)),
|
||||
new HashSet<Object>(presenter.getSelectedComponents()));
|
||||
assertEquals(new HashSet<Integer>(Arrays.asList(LayerIds.BOTTOM_TRACES)),
|
||||
presenter.getCurrentProject().getLockedLayers());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothSidesKeepTheLegacyLayerForOlderVersions() {
|
||||
Project project = presenter.getCurrentProject();
|
||||
presenter.setLayerVisibility(LayerIds.BOTTOM_TRACES, false);
|
||||
presenter.setLayerVisibility(LayerIds.TOP_TRACES, false);
|
||||
assertTrue(project.getHiddenLayers().contains(LayerIds.LEGACY_TRACES));
|
||||
|
||||
presenter.setLayerVisibility(LayerIds.TOP_TRACES, true);
|
||||
assertEquals(new HashSet<Integer>(Arrays.asList(LayerIds.BOTTOM_TRACES)),
|
||||
project.getHiddenLayers());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTraceMovedToALockedSideIsLocked() {
|
||||
presenter.setLayerLocked(LayerIds.BOTTOM_TRACES, true);
|
||||
presenter.selectAll(0);
|
||||
assertTrue(presenter.getSelectedComponents().contains(topTrace));
|
||||
|
||||
List<PropertyWrapper> properties = presenter.getProperties(topTrace);
|
||||
for (PropertyWrapper property : properties) {
|
||||
if (property.getName().equals("Layer")) {
|
||||
property.setValue(PCBLayer._1);
|
||||
}
|
||||
}
|
||||
presenter.applyProperties(topTrace, properties);
|
||||
|
||||
presenter.selectAll(0);
|
||||
assertFalse(presenter.getSelectedComponents().contains(topTrace));
|
||||
}
|
||||
|
||||
private static CopperTrace trace(PCBLayer layer) {
|
||||
CopperTrace trace = new CopperTrace();
|
||||
trace.setLayer(layer);
|
||||
return trace;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ that copper on one side of the board can be hidden, locked or selected without t
|
||||
Two-sided boards, the Underside Display editing and the Via made one trace layer too coarse: a user
|
||||
routing the bottom in the Underside Display wants to lock the top, and the other way round.
|
||||
|
||||
Status: planned, nothing built yet.
|
||||
Status: done. Core, library and swing tests pass; the regression suite has not been run, and no
|
||||
change is expected there (section 5).
|
||||
|
||||
## 1. Background
|
||||
|
||||
@@ -37,6 +38,8 @@ Status: planned, nothing built yet.
|
||||
| The Visible shortcuts are renumbered to follow the menu: Ctrl+1 Chassis, Ctrl+2 Board, Ctrl+3 Bottom Traces, Ctrl+4 Top Traces, Ctrl+5 Component, Ctrl+6 Wiring, Ctrl+7 Text | Shortcuts that follow the menu order are easier to learn than a new one added out of order. Only the shortcuts move; the saved layer ids do not, so files are unaffected. |
|
||||
| New ids: Bottom Traces `31`, Top Traces `32` (`TRACE * 10 + PCBLayer.getId()`) | They cannot collide with a z-order. Reusing `3` for one side would make an older DIYLC treat a file with only one side hidden as all traces hidden. |
|
||||
| `3` stays as the legacy id meaning both trace layers, and the saved sets keep the invariant "`3` is present exactly when `31` and `32` both are" | A file saved with both sides hidden or locked still hides or locks all traces in an older DIYLC, and an older DIYLC that unhides or unlocks Trace is understood by the new one (section 4). |
|
||||
| The ids and the set rules live in `org.diylc.core.LayerIds`; which layer a component is on lives in `org.diylc.presenter.ComponentLayers` | `Project` applies the rules on load and must not depend on the presenter package; layer membership needs the z-order from `ComponentProcessor`. |
|
||||
| Two set rules, not one: `migrateLoaded` after a load, `syncLegacy` after a change | "Both sides set, no `3`" means opposite things in the two cases: written by an older version that cleared Trace, or just set by the user. |
|
||||
| Copper with a side is on the layer of that side; the deprecated inner layers count as Bottom | `IPCBLayerComponent.getBoardSide()` already maps them that way. |
|
||||
| A Trace-band component that reports layer id `0` is on both trace layers: a PTH `SolderPad`, and `Via`, `Eyelet` and `Turret`, which implement `ILayeredComponent` to say so. A composite whose z-order falls in the Trace band is on both as well | Layer id `0` already means "every layer" to continuity, and the three go through the board. A composite may hold copper of both sides. Core names no concrete component. |
|
||||
| Every other Trace-band component is on the layer of `IBoardSideComponent.sideOf`, so a component without a side, such as `Jumper`, is on Top Traces | `sideOf` already treats a component without a side as top, the same rule the rendering uses. A jumper sits on top of the board: locking Bottom Traces to work on the top must leave it movable. |
|
||||
@@ -48,22 +51,23 @@ Status: planned, nothing built yet.
|
||||
|
||||
### diylc-core
|
||||
|
||||
- `org.diylc.presenter.ComponentLayers` (new, not in `Presenter`, which stays lean):
|
||||
- constants `BOTTOM_TRACES = 31`, `TOP_TRACES = 32`, and `LEGACY_TRACES = IDIYComponent.TRACE`;
|
||||
- `Set<Integer> getLayers(IDIYComponent<?>)`: the rounded z-order for everything outside the
|
||||
Trace band; inside it, both trace ids for layer id `0` and for a composite, otherwise the id
|
||||
of `IBoardSideComponent.sideOf`;
|
||||
- `isHidden(Project, component)` (all its layers hidden), `isLocked(Project, component)` (any of
|
||||
its layers locked) and `isOnLayer(component, layerId)` for Select All;
|
||||
- `normalize(Set<Integer>)`, which applies the invariant of section 4 and is used both after
|
||||
loading and after every change.
|
||||
- `org.diylc.core.LayerIds` (new): `LEGACY_TRACES = IDIYComponent.TRACE`, `BOTTOM_TRACES = 31`,
|
||||
`TOP_TRACES = 32`, `migrateLoaded(Set<Integer>)` for a set read from a file and
|
||||
`syncLegacy(Set<Integer>)` for a set just changed (section 4).
|
||||
- `org.diylc.presenter.ComponentLayers` (new, not in `Presenter`, which stays lean): the layer of a
|
||||
component is the rounded z-order outside the Trace band; inside it, both trace layers for layer
|
||||
id `0` and for a composite, otherwise the layer of `IBoardSideComponent.sideOf`. It answers
|
||||
`isHidden(hiddenLayers, component)` (all its layers hidden), `isLocked(lockedLayers, component)`
|
||||
(any of its layers locked) and `isOnLayer(component, layerId)` for Select All, without
|
||||
allocating, since drawing asks for every component on every repaint.
|
||||
- `Presenter.isComponentVisible`, `isComponentLayerLocked`, `selectAll(int)` and
|
||||
`GroupAndLockIndex.isLocked` call the helper. Internal types without a descriptor stay visible
|
||||
and unlocked, as today.
|
||||
- `Presenter.setLayerLocked` and `setLayerVisibility` call `normalize` on the changed set.
|
||||
- `Project.readResolve()` (new) normalizes `lockedLayers` and `hiddenLayers` whenever XStream
|
||||
loads a project, so the migration does not depend on which code path opened the file. XStream
|
||||
calls `readResolve` on any class that declares it.
|
||||
- `Presenter.setLayerLocked` and `setLayerVisibility` call `syncLegacy` on the changed set; given
|
||||
the legacy id `3`, they change both trace layers.
|
||||
- `Project.readResolve()` (new) runs `migrateLoaded` on `lockedLayers` and `hiddenLayers`
|
||||
whenever XStream loads a project, so the migration does not depend on which code path opened
|
||||
the file. XStream calls `readResolve` on any class that declares it.
|
||||
- `Via`, `Eyelet` and `Turret` (diylc-library) implement `ILayeredComponent` with `getLayerId()`
|
||||
returning `0`. Continuity does not change: a component that is not layered already counts as
|
||||
layer `0`.
|
||||
@@ -87,8 +91,8 @@ Status: planned, nothing built yet.
|
||||
Nothing about components changes, and no field is added, renamed or retyped. The only saved data
|
||||
involved are the two `Set<Integer>` fields of `Project`, whose type stays the same.
|
||||
|
||||
**Invariant.** In a saved set, `3` is present exactly when `31` and `32` both are. `normalize`
|
||||
enforces it:
|
||||
**Invariant.** In a saved set, `3` is present exactly when `31` and `32` both are. `migrateLoaded`
|
||||
applies the first three rows after a load, `syncLegacy` the last one after a change:
|
||||
|
||||
| Set as loaded or changed | Normalized | Why |
|
||||
| --- | --- | --- |
|
||||
@@ -117,22 +121,21 @@ clipboard carries components, not layer sets.
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- `ComponentLayersTest`: a top and a bottom trace, an inner-layer trace (Bottom), PTH and NPTH
|
||||
pads, a `Via`, an `Eyelet`, a `Jumper` (Top Traces) and a Trace-band composite each land on the
|
||||
expected layers; a
|
||||
Component-band part keeps its z-order layer.
|
||||
- `ComponentLayersTest` (library): a top and a bottom trace, an inner-layer trace (Bottom), PTH and
|
||||
NPTH pads, a `Via`, an `Eyelet`, a `Jumper` (Top Traces) and a Trace-band composite each land on
|
||||
the expected layers; a Component-band part keeps its z-order layer.
|
||||
- Hidden and locked rules: a via is hidden only with both sides hidden and locked with either side
|
||||
locked; a jumper stays unlocked when only Bottom Traces is locked.
|
||||
- Continuity: the netlist of a project with vias, eyelets and turrets is unchanged.
|
||||
- `normalize`: each row of the table in section 4, and that a loaded project goes through it
|
||||
(`readResolve`), using an XML project with `<int>3</int>` in `hiddenLayers` and `lockedLayers`.
|
||||
- Presenter: Select All per trace layer; locking Bottom Traces and then moving a selected top trace
|
||||
to the bottom leaves it locked.
|
||||
- `LayerIdsTest` (core): each row of the table in section 4, and a project saved and loaded through
|
||||
XStream, so that `readResolve` runs.
|
||||
- `TraceLayerTests` (swing): Select All per trace layer; locking Bottom Traces locks the via but
|
||||
not the top trace or the jumper; both sides hidden keeps `3`; a top trace moved to the locked
|
||||
bottom through its Layer property is locked.
|
||||
- Regression suite: no image or netlist change expected, since no regression project hides or
|
||||
locks the Trace layer.
|
||||
|
||||
## 6. Next steps
|
||||
|
||||
1. Build the core helper with its tests, the `ILayeredComponent` declarations on `Via`, `Eyelet`
|
||||
and `Turret`, then the menu and the language keys.
|
||||
2. Changelog entry in the unreleased version, including the renumbered shortcuts.
|
||||
1. Check the menu, the shortcuts and the locking in the running app, and open a project saved with
|
||||
the old Trace layer hidden or locked.
|
||||
|
||||
Reference in New Issue
Block a user