workcraft/CircuitPlugin/src/org/workcraft/plugins/circuit/utils/SpaceUtils.java
package org.workcraft.plugins.circuit.utils;
import org.workcraft.dom.Node;
import org.workcraft.dom.visual.*;
import org.workcraft.dom.visual.connections.ConnectionUtils;
import org.workcraft.dom.visual.connections.ControlPoint;
import org.workcraft.dom.visual.connections.VisualConnection;
import org.workcraft.plugins.circuit.*;
import org.workcraft.utils.Hierarchy;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
public final class SpaceUtils {
private SpaceUtils() {
}
public static void makeSpaceAfterContact(VisualCircuit circuit, VisualContact contact, double space) {
// Change connection scale mode to LOCK_RELATIVELY for cleaner relocation of components
Collection<VisualConnection> connections = Hierarchy.getDescendantsOfType(circuit.getRoot(), VisualConnection.class);
HashMap<VisualConnection, VisualConnection.ScaleMode> connectionToScaleModeMap
= ConnectionUtils.replaceConnectionScaleMode(connections, VisualConnection.ScaleMode.LOCK_RELATIVELY);
SpaceUtils.offsetComponentsFromContact(circuit, contact, space + 1.0);
VisualJoint joint = CircuitUtils.detachJoint(circuit, contact);
if (joint != null) {
joint.setRootSpacePosition(getOffsetContactPosition(contact, space));
}
// Restore connection scale mode
ConnectionUtils.restoreConnectionScaleMode(connectionToScaleModeMap);
}
public static void offsetComponentsFromContact(VisualCircuit circuit, VisualContact contact, double space) {
double minSpace = getMinSpace(circuit, contact);
if (minSpace > space) {
return;
}
int dx = contact.getDirection().getGradientX();
int dy = contact.getDirection().getGradientY();
double x0 = contact.getRootSpaceX();
double y0 = contact.getRootSpaceY();
double xSpace = dx * (space - minSpace);
double ySpace = dy * (space - minSpace);
Collection<VisualTransformableNode> transformableNodes = Hierarchy.getDescendantsOfType(circuit.getRoot(),
VisualTransformableNode.class, node -> !(node instanceof VisualPage)
&& !(node instanceof ControlPoint)
&& !((node instanceof VisualContact) && ((VisualContact) node).isPin()));
for (VisualTransformableNode transformableNode : transformableNodes) {
Rectangle2D bb = (transformableNode instanceof VisualComponent)
? ((VisualComponent) transformableNode).getInternalBoundingBoxInLocalSpace()
: transformableNode.getBoundingBoxInLocalSpace();
double x = transformableNode.getRootSpaceX();
double xBorder = x + bb.getX() + ((dx < 0) ? bb.getWidth() : 0.0);
if ((xBorder - x0) * dx > 0) {
transformableNode.setRootSpaceX(x + xSpace);
}
double y = transformableNode.getRootSpaceY();
double yBorder = y + bb.getY() + ((dy < 0) ? bb.getHeight() : 0.0);
if ((yBorder - y0) * dy > 0) {
transformableNode.setRootSpaceY(y + ySpace);
}
}
}
private static double getMinSpace(VisualCircuit circuit, VisualContact contact) {
double result = Double.MAX_VALUE;
int dx = contact.getDirection().getGradientX();
int dy = contact.getDirection().getGradientY();
double x0 = contact.getRootSpaceX();
double y0 = contact.getRootSpaceY();
for (Node node : circuit.getPostset(contact)) {
if (node instanceof VisualComponent) {
VisualComponent component = (VisualComponent) node;
Rectangle2D bb = component.getInternalBoundingBoxInLocalSpace();
double x = component.getRootSpaceX();
double xBorder = x + bb.getX() + ((dx < 0) ? bb.getWidth() : 0.0);
double xSpace = Math.abs(xBorder - x0);
if (((xBorder - x0) * dx > 0) && (xSpace < result)) {
result = xSpace;
}
double y = component.getRootSpaceY();
double yBorder = y + bb.getY() + ((dy < 0) ? bb.getHeight() : 0.0);
double ySpace = Math.abs(yBorder - y0);
if (((yBorder - y0) * dy > 0) && (ySpace < result)) {
result = ySpace;
}
}
}
return result;
}
public static Point2D getOffsetContactPosition(VisualContact contact, double space) {
double d = contact.isPort() ? -space : space;
double x = contact.getRootSpaceX() + d * contact.getDirection().getGradientX();
double y = contact.getRootSpaceY() + d * contact.getDirection().getGradientY();
return new Point2D.Double(x, y);
}
public static void positionPort(VisualCircuit circuit, VisualContact port, boolean alignToRight) {
Collection<Touchable> nodes = new HashSet<>(Hierarchy.getChildrenOfType(circuit.getRoot(), VisualConnection.class));
for (VisualConnection connection : circuit.getConnections(port)) {
nodes.remove(connection);
}
nodes.addAll(Hierarchy.getChildrenOfType(circuit.getRoot(), VisualCircuitComponent.class));
nodes.addAll(Hierarchy.getChildrenOfType(circuit.getRoot(), VisualJoint.class));
Rectangle2D modelBox = BoundingBoxHelper.mergeBoundingBoxes(nodes);
double x = TransformHelper.snapP5(alignToRight ? modelBox.getMaxX() : modelBox.getMinX());
double y = modelBox.getCenterY();
if (port.isOutput()) {
VisualContact driver = CircuitUtils.findDriver(circuit, port, false);
if (driver != null) {
y = driver.getRootSpaceY();
}
} else {
Collection<VisualContact> driven = CircuitUtils.findDriven(circuit, port, false);
if (driven.size() == 1) {
y = driven.iterator().next().getRootSpaceY();
} else if (!driven.isEmpty()) {
Point2D p = MixUtils.middleRootspacePosition(driven);
if (p != null) {
y = TransformHelper.snapP5(p.getY());
}
}
}
boolean done = false;
while (!done) {
done = true;
for (VisualContact otherPort : circuit.getVisualPorts()) {
if ((port != otherPort) && (otherPort.getRootSpacePosition().distance(x, y) < 0.1)) {
y = TransformHelper.snapP5(y + 0.5);
done = false;
}
}
}
port.setRootSpacePosition(new Point2D.Double(x, y));
}
public static void detachAndPositionJoint(VisualCircuit circuit, VisualContact port) {
VisualJoint joint = CircuitUtils.detachJoint(circuit, port);
if (joint != null) {
joint.setRootSpacePosition(getOffsetContactPosition(port, 0.5));
}
}
public static List<VisualFunctionComponent> orderComponentsByPosition(List<VisualFunctionComponent> components) {
return components.stream()
.sorted((o1, o2) -> {
int xComparison = Double.compare(o2.getRootSpaceX(), o1.getRootSpaceX());
return xComparison == 0 ? Double.compare(o2.getRootSpaceY(), o1.getRootSpaceY()) : xComparison;
})
.collect(Collectors.toList());
}
}