Derive the z-order of a building blocks placed as a single component based on its child components

This commit is contained in:
Bane Stojkovic
2026-11-01 22:56:48 +01:00
parent 79d230ff07
commit b15a6e3f64
9 changed files with 455 additions and 55 deletions
@@ -118,16 +118,10 @@ public class ComparatorFactory {
if (componentZOrderComparator == null) {
componentZOrderComparator = new Comparator<IDIYComponent<?>>() {
@SuppressWarnings("unchecked")
@Override
public int compare(IDIYComponent<?> o1, IDIYComponent<?> o2) {
ComponentType type1 =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) o1.getClass());
ComponentType type2 =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) o2.getClass());
return Double.valueOf(type1.getZOrder()).compareTo(type2.getZOrder());
return Double.compare(ComponentProcessor.getInstance().getZOrder(o1),
ComponentProcessor.getInstance().getZOrder(o2));
}
};
}
@@ -409,6 +409,31 @@ public class ComponentProcessor {
return type.getName();
}
/**
* @return the z-order (layer) of the component. A {@link CompositeComponent} sits on the lowest
* layer among its children rather than the one its class declares: it is drawn as a
* single unit, so it takes one place in the stack, and that place has to be under
* whatever is later put on top of it. A block of chassis panels therefore stays on the
* chassis layer, and a block built from a board, pads and silkscreen text on the board
* layer.
*/
@SuppressWarnings("unchecked")
public double getZOrder(IDIYComponent<?> component) {
if (component instanceof CompositeComponent) {
double zOrder = Double.MAX_VALUE;
for (IDIYComponent<?> child : ((CompositeComponent) component).getChildComponents()) {
zOrder = Math.min(zOrder, getZOrder(child));
}
if (zOrder < Double.MAX_VALUE) {
return zOrder;
}
}
ComponentType type =
extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
// internal types without a descriptor do not take part in layering
return type == null ? Double.MAX_VALUE : type.getZOrder();
}
public static boolean hasStickyPoint(IDIYComponent<?> c) {
for (int i = 0; i < c.getControlPointCount(); i++)
if (c.isControlPointSticky(0))
@@ -94,7 +94,8 @@ public class GroupAndLockIndex {
if (componentType == null) {
return false;
}
return project.getLockedLayers().contains((int) Math.round(componentType.getZOrder()))
return project.getLockedLayers()
.contains((int) Math.round(ComponentProcessor.getInstance().getZOrder(component)))
|| project.getLockedComponents().contains(component);
}
@@ -494,7 +494,8 @@ public class Presenter implements IPlugInPort {
// for internal types
if (componentType == null)
return true;
return !currentProject.getHiddenLayers().contains((int) Math.round(componentType.getZOrder()));
return !currentProject.getHiddenLayers()
.contains((int) Math.round(ComponentProcessor.getInstance().getZOrder(component)));
}
@Override
@@ -1076,10 +1077,9 @@ public class Presenter implements IPlugInPort {
Iterator<IDIYComponent<?>> i = newSelection.iterator();
while (i.hasNext()) {
IDIYComponent<?> c = i.next();
ComponentType type =
ComponentProcessor.getInstance().extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass());
if ((int) type.getZOrder() != layer)
if ((int) ComponentProcessor.getInstance().getZOrder(c) != layer) {
i.remove();
}
}
}
updateSelection(newSelection);
@@ -2229,6 +2229,7 @@ public class Presenter implements IPlugInPort {
ComponentType componentType =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) component.getClass());
long layer = Math.round(ComponentProcessor.getInstance().getZOrder(component));
int index = currentProject.getComponents().indexOf(component);
if (index < 0) {
LOG.warn("Component not found in the project: " + component.getName());
@@ -2236,11 +2237,8 @@ public class Presenter implements IPlugInPort {
while (index > 0) {
IDIYComponent<?> componentBefore = currentProject.getComponents().get(index - 1);
if (!selectedComponents.contains(componentBefore)) {
ComponentType componentBeforeType =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) componentBefore.getClass());
if (!componentType.isFlexibleZOrder()
&& Math.round(componentBeforeType.getZOrder()) < Math.round(componentType.getZOrder())
&& Math.round(ComponentProcessor.getInstance().getZOrder(componentBefore)) < layer
&& forceConfirmation != IView.YES_OPTION
&& (forceConfirmation =
this.view
@@ -2278,6 +2276,7 @@ public class Presenter implements IPlugInPort {
ComponentType componentType =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) component.getClass());
long layer = Math.round(ComponentProcessor.getInstance().getZOrder(component));
int index = currentProject.getComponents().indexOf(component);
if (index < 0) {
LOG.warn("Component not found in the project: " + component.getName());
@@ -2285,11 +2284,8 @@ public class Presenter implements IPlugInPort {
while (index < currentProject.getComponents().size() - 1) {
IDIYComponent<?> componentAfter = currentProject.getComponents().get(index + 1);
if (!selectedComponents.contains(componentAfter)) {
ComponentType componentAfterType =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) componentAfter.getClass());
if (!componentType.isFlexibleZOrder()
&& Math.round(componentAfterType.getZOrder()) > Math.round(componentType.getZOrder())
&& Math.round(ComponentProcessor.getInstance().getZOrder(componentAfter)) > layer
&& forceConfirmation != IView.YES_OPTION
&& (forceConfirmation =
this.view
@@ -2684,13 +2680,9 @@ public class Presenter implements IPlugInPort {
@SuppressWarnings("unchecked")
private void addComponent(IDIYComponent<?> component, boolean allowAutoCreate) {
int index = currentProject.getComponents().size();
while (index > 0
&& ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass()).getZOrder() < ComponentProcessor
.getInstance()
.extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) currentProject.getComponents().get(index - 1).getClass())
.getZOrder()) {
double zOrder = ComponentProcessor.getInstance().getZOrder(component);
while (index > 0 && zOrder < ComponentProcessor.getInstance()
.getZOrder(currentProject.getComponents().get(index - 1))) {
index--;
}
if (index < currentProject.getComponents().size()) {
@@ -2724,12 +2716,9 @@ public class Presenter implements IPlugInPort {
int maxIndex = 0;
for (IDIYComponent<?> component : components) {
int index = currentProject.getComponents().size();
while (index > maxIndex && ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass())
.getZOrder() < ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) currentProject
.getComponents().get(index - 1).getClass())
.getZOrder()) {
double zOrder = ComponentProcessor.getInstance().getZOrder(component);
while (index > maxIndex && zOrder < ComponentProcessor.getInstance()
.getZOrder(currentProject.getComponents().get(index - 1))) {
index--;
}
@@ -3132,12 +3121,9 @@ public class Presenter implements IPlugInPort {
return GroupAndLockIndex.isLocked(currentProject, component);
}
@SuppressWarnings("unchecked")
private boolean isComponentLayerLocked(IDIYComponent<?> component) {
ComponentType componentType =
ComponentProcessor.getInstance().extractComponentTypeFrom(
(Class<? extends IDIYComponent<?>>) component.getClass());
return currentProject.getLockedLayers().contains((int) Math.round(componentType.getZOrder()));
return currentProject.getLockedLayers()
.contains((int) Math.round(ComponentProcessor.getInstance().getZOrder(component)));
}
/**
@@ -517,6 +517,10 @@
<changeType>IMPROVEMENT</changeType>
<description>Re-modeled factory variants of Resistor component type as datasheet in order to make them available to the AI; this will produce more accurate AI-driven edits</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Derive the z-order of a building blocks placed as a single component based on its child components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added datasheets to 'DIP IC', 'SIP IC' and 'Multiwatt IC' - pick a part by its number from the component's submenu to set its pin count, spacing and pin names at once; the AI Assistant can place these parts by part number too</description>
@@ -35,7 +35,12 @@ import java.util.UUID;
import org.diylc.common.Percentage;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.netlist.Node;
import org.diylc.presenter.ComparatorFactory;
import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.GroupAndLockIndex;
import org.diylc.serialization.ProjectFileManager;
import org.diylc.testcomponents.OnePointTestComponent;
import org.diylc.testcomponents.TwoPointTestComponent;
@@ -326,4 +331,77 @@ public class CompositeComponentTest {
assertNotNull(blank.getChildComponents());
assertTrue(blank.getChildComponents().isEmpty());
}
@Test
public void layerOfAChassisOnlyCompositeIsTheChassisLayer() {
CompositeComponent composite = customBoard(new ChassisTestComponent(),
new ChassisTestComponent());
assertEquals(IDIYComponent.CHASSIS, ComponentProcessor.getInstance().getZOrder(composite), 0);
}
@Test
public void layerOfAMixedCompositeIsItsLowestChildLayer() {
CompositeComponent composite = customBoard(new TextTestComponent(), twoPoint("R1"),
new BoardTestComponent());
assertEquals(IDIYComponent.BOARD, ComponentProcessor.getInstance().getZOrder(composite), 0);
}
@Test
public void layerOfANestedCompositeLooksThroughTheInnerComposite() {
CompositeComponent inner = customBoard(new ChassisTestComponent());
CompositeComponent outer = customBoard(twoPoint("R1"), inner);
assertEquals(IDIYComponent.CHASSIS, ComponentProcessor.getInstance().getZOrder(outer), 0);
}
@Test
public void layerOfAChildlessCompositeIsTheDeclaredOne() {
assertEquals(IDIYComponent.COMPONENT,
ComponentProcessor.getInstance().getZOrder(new CompositeComponent()), 0);
}
@Test
public void lockingTheChildLayerLocksTheComposite() {
Project project = new Project();
CompositeComponent chassis = customBoard(new ChassisTestComponent());
project.getComponents().add(chassis);
project.getLockedLayers().add(IDIYComponent.COMPONENT);
assertFalse(GroupAndLockIndex.isLocked(project, chassis));
project.getLockedLayers().add(IDIYComponent.CHASSIS);
assertTrue(GroupAndLockIndex.isLocked(project, chassis));
}
@Test
public void compositeSortsByItsChildLayer() {
CompositeComponent chassis = customBoard(new ChassisTestComponent());
BoardTestComponent board = new BoardTestComponent();
assertTrue(ComparatorFactory.getInstance().getComponentZOrderComparator().compare(chassis,
board) < 0);
}
@ComponentDescriptor(name = "Chassis Test", category = "Test", author = "Test",
description = "test", instanceNamePrefix = "CT", zOrder = IDIYComponent.CHASSIS)
public static class ChassisTestComponent extends TwoPointTestComponent {
private static final long serialVersionUID = 1L;
}
@ComponentDescriptor(name = "Board Test", category = "Test", author = "Test",
description = "test", instanceNamePrefix = "BT", zOrder = IDIYComponent.BOARD)
public static class BoardTestComponent extends TwoPointTestComponent {
private static final long serialVersionUID = 1L;
}
@ComponentDescriptor(name = "Text Test", category = "Test", author = "Test",
description = "test", instanceNamePrefix = "TT", zOrder = IDIYComponent.TEXT)
public static class TextTestComponent extends TwoPointTestComponent {
private static final long serialVersionUID = 1L;
}
}