Skip to content

Commit

Permalink
[286] Use figure bounds intersection as edge anchor
Browse files Browse the repository at this point in the history
Bug: #286
Signed-off-by: Florian Barbin <florian.barbin@obeo.fr>
  • Loading branch information
florianbarbin committed Mar 3, 2021
1 parent aa9dbb7 commit dad4389
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
*******************************************************************************/
package org.eclipse.sirius.web.diagrams.layout.incremental.provider;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.eclipse.sirius.web.diagrams.Position;
import org.eclipse.sirius.web.diagrams.Size;
import org.eclipse.sirius.web.diagrams.layout.incremental.data.EdgeLayoutData;
import org.eclipse.sirius.web.diagrams.layout.incremental.data.NodeLayoutData;
import org.eclipse.sirius.web.diagrams.layout.incremental.utils.Bounds;
import org.eclipse.sirius.web.diagrams.layout.incremental.utils.Geometry;

/**
* Provides the routing points to apply to an Edge.
Expand All @@ -28,21 +29,36 @@
public class EdgeRoutingPointsProvider {

public List<Position> getRoutingPoints(EdgeLayoutData edge) {
List<Position> routingPoints = new ArrayList<>();
routingPoints.add(this.getCenter(edge.getSource()));
routingPoints.add(this.getCenter(edge.getTarget()));
return routingPoints;
Bounds sourceBounds = this.getAbsoluteBounds(edge.getSource());
Bounds targetBounds = this.getAbsoluteBounds(edge.getTarget());
Position sourceAbsolutePosition = this.getCenter(sourceBounds);
Position targetAbsolutePosition = this.getCenter(targetBounds);
Geometry geometry = new Geometry();
Optional<Position> optionalSourceIntersection = geometry.getIntersection(targetAbsolutePosition, sourceAbsolutePosition, sourceBounds);
Optional<Position> optionalTargetIntersection = geometry.getIntersection(sourceAbsolutePosition, targetAbsolutePosition, targetBounds);
if (optionalSourceIntersection.isPresent() && optionalTargetIntersection.isPresent()) {
return List.of(optionalSourceIntersection.get(), optionalTargetIntersection.get());
}
return List.of();
}

private Position getCenter(NodeLayoutData nodeLayoutData) {
Position absoluteNodePosition = nodeLayoutData.getAbsolutePosition();
Size nodeSize = nodeLayoutData.getSize();
private Bounds getAbsoluteBounds(NodeLayoutData nodeLayoutData) {
// @formatter:off
return Bounds.newBounds()
.position(nodeLayoutData.getAbsolutePosition())
.size(nodeLayoutData.getSize())
.build();
// @formatter:on
}

private Position getCenter(Bounds bounds) {
// @formatter:off
return Position.newPosition()
.x(absoluteNodePosition.getX() + (nodeSize.getWidth() / 2))
.y(absoluteNodePosition.getY() + (nodeSize.getHeight() / 2))
.x(bounds.getPosition().getX() + (bounds.getSize().getWidth() / 2))
.y(bounds.getPosition().getY() + (bounds.getSize().getHeight() / 2))
.build();
// @formatter:on

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.eclipse.sirius.web.diagrams.Position;
import org.eclipse.sirius.web.diagrams.Size;
import org.eclipse.sirius.web.diagrams.layout.incremental.IncrementalLayoutEngine;
import org.eclipse.sirius.web.diagrams.layout.incremental.data.IContainerLayoutData;
import org.eclipse.sirius.web.diagrams.layout.incremental.data.NodeLayoutData;
import org.eclipse.sirius.web.diagrams.layout.incremental.utils.Bounds;
import org.eclipse.sirius.web.diagrams.layout.incremental.utils.Geometry;

/**
* An algorithm dedicated to solve overlaps issues. Any node in a given container might be moved to avoid overlaps,
Expand Down Expand Up @@ -118,109 +121,35 @@ private double computeDistance(Position source, Position target) {
}

/**
* Computes the distance between the center
* Computes the shortest distance between the center1 point and the given node bounds intersection with the segment
* (center1, center2).
*
* @param center1
* the first node center.
* @param center2
* the second node center.
* @param node
* @return
* the node used for computing the intersection.
* @return the shortest distance.
*/
private double computeDistanceToBorder(Position center1, Position center2, NodeLayoutData node) {
Position intersection = this.getIntersection(center1, center2, node, center2);
return this.computeDistance(center1, intersection);
}

/**
* Finds the closest intersection from a given point, along a given line, with a rectangle.
*
* @param lineA
* the line first point
* @param lineB
* the line second point
* @param closest
* the closest point to the intersection
* @return the intersection
*/
private Position getIntersection(Position lineA, Position lineB, NodeLayoutData node, Position closest) {
Position topLeft = node.getPosition();
Position topRight = Position.newPosition().x(node.getPosition().getX() + node.getSize().getWidth()).y(node.getPosition().getY()).build();
Position bottomLeft = Position.newPosition().x(node.getPosition().getX()).y(node.getPosition().getY() + node.getSize().getHeight()).build();
Position bottomRight = Position.newPosition().x(node.getPosition().getX() + node.getSize().getWidth()).y(node.getPosition().getY() + node.getSize().getHeight()).build();

Position topIntersection = this.getIntersection(lineA, lineB, topLeft, topRight);
Position bottomIntersection = this.getIntersection(lineA, lineB, bottomLeft, bottomRight);
Position leftIntersection = this.getIntersection(lineA, lineB, topLeft, bottomLeft);
Position rightIntersection = this.getIntersection(lineA, lineB, topRight, bottomRight);

Position minDistancePosition = this.getMinDistancePosition(closest, topIntersection, bottomIntersection, leftIntersection, rightIntersection);
return minDistancePosition;

}

/**
* Returns the closest point from the source.
*
* @param source
* the source point
* @param candidates
* the target candidates
* @return the closest point
*/
private Position getMinDistancePosition(Position source, Position... candidates) {
Position minPosition = null;
Double minDistance = null;
for (Position intersection : candidates) {
if (intersection != null) {
double tmp = this.computeDistance(source, intersection);
if (minDistance == null) {
minDistance = tmp;
minPosition = intersection;
} else if (tmp < minDistance) {
minDistance = tmp;
minPosition = intersection;
}
}
Geometry geometry = new Geometry();
Optional<Position> optionalIntersection = geometry.getIntersection(center2, center1, this.getBounds(node));
if (optionalIntersection.isPresent()) {
return this.computeDistance(center1, optionalIntersection.get());
} else {
return 0;
}
return minPosition;
}

/**
* Returns the intersection between a line and a segment.
*
* @param lineA
* the line first point
* @param lineB
* the line second point
* @param segmentSource
* the segment source
* @param segmentTarget
* the segment target
* @return the intersection or <null>
*/
private Position getIntersection(Position lineA, Position lineB, Position segmentSource, Position segmentTarget) {
double x1 = lineA.getX();
double y1 = lineA.getY();
double x2 = lineB.getX();
double y2 = lineB.getY();

double x3 = segmentSource.getX();
double y3 = segmentSource.getY();
double x4 = segmentTarget.getX();
double y4 = segmentTarget.getY();

Position p = null;
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d != 0) {
double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
if (x3 == x4 && yi <= y4 && yi >= y3) {
p = Position.newPosition().x(xi).y(yi).build();
}
if (y3 == y4 && xi <= x4 && xi >= x3) {
p = Position.newPosition().x(xi).y(yi).build();
}
}
return p;
private Bounds getBounds(NodeLayoutData nodeLayoutData) {
// @formatter:off
return Bounds.newBounds()
.position(nodeLayoutData.getPosition())
.size(nodeLayoutData.getSize())
.build();
// @formatter:on
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright (c) 2021 THALES GLOBAL SERVICES.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.web.diagrams.layout.incremental.utils;

import java.text.MessageFormat;
import java.util.Objects;

import org.eclipse.sirius.web.annotations.Immutable;
import org.eclipse.sirius.web.diagrams.Position;
import org.eclipse.sirius.web.diagrams.Size;

/**
* The bounds (Size and Position) of an element.
*
* @author fbarbin
*/
@Immutable
public final class Bounds {

private Size size;

private Position position;

private Bounds() {
// Prevent instantiation
}

public Size getSize() {
return this.size;
}

public Position getPosition() {
return this.position;
}

public static Builder newBounds() {
return new Builder();
}

@Override
public String toString() {
String pattern = "{0} '{'size: {1}, position: {2}'}'"; //$NON-NLS-1$
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.size, this.position);
}

/**
* The builder used to create a new bounds.
*
* @author fbarbin
*/
@SuppressWarnings("checkstyle:HiddenField")
public static final class Builder {

private Size size;

private Position position;

private Builder() {
// Prevent instantiation
}

public Builder size(Size size) {
this.size = Objects.requireNonNull(size);
return this;
}

public Builder position(Position position) {
this.position = Objects.requireNonNull(position);
return this;
}

public Bounds build() {
Bounds position = new Bounds();
position.size = Objects.requireNonNull(this.size);
position.position = Objects.requireNonNull(this.position);
return position;
}
}
}
Loading

0 comments on commit dad4389

Please sign in to comment.