# Introduction

This short article aims at helping CASE tool developers to handle properly the update of connections between boxes when they are moved.

Most tools do it wrong and the results is that users have to spend a lot of time placing arrows at the right place.

Author: Maxime Perrotin

# Expected output

We aim at allowing all kinds of connections - straight lignes or multiple angles, and we want that when moving a block, the overall diagram respects the connection shape given by the user.

This is the example for which we are going to provide the algorithm:

# Algorithm

The main idea is that the shapes have to keep their proportions when the block moves away or gets closer.

When creating a connection made of multiple points, we must not only store the coordinates of the points themselves, but also their proportional distance to the starting point. When the block is moved, the proportion can be applied back. There are a few special cases to keep right angles on the first or last connection point in place.

## When the connection is created

The following Python snippet shows what to store and how. It is based on PySide2, but the same would apply with Qt/C++ or any other GUI library.

```   @middle_points.setter
def middle_points(self, points_scene_coord):
# compute the distance between the start and end points
dist_x = abs(self.end_point.x() - self.start_point.x())
dist_y = abs(self.end_point.y() - self.start_point.y())
# Compute the distance ratio
self._ratios = []
for point in points_scene_coord:
pCoord = self.parent.mapFromScene(point)
len_x = abs(pCoord.x() - self.start_point.x())
len_y = abs(pCoord.y() - self.start_point.y())
fact_x = 1 if dist_x == 0 else len_x / dist_x
fact_y = 1 if dist_y == 0 else len_y / dist_y
if pCoord.y() == self.start_point.y():
fact_y = -fact_y
if pCoord.x() == self.start_point.x():
fact_x = -fact_x
self._ratios.append((fact_x, fact_y))
self._middle_points = points_scene_coord
```

## When the destination block is moved

The connection's parent is the source block. You have to pay attention to use the right coordinate system.

```   @Slot(float, float)
def child_moved(self, delta_x, delta_y):

# Move the end point according to the mouse movement
self._end_point.setX(self._end_point.x() - delta_x)
self._end_point.setY(self._end_point.y() - delta_y)

# Get the current position of each middle point
middle_points = list(self.middle_points)

# Start from a new list of middle points to compute new coordinates
self._middle_points = []

for ratio, point in zip(self._ratios, middle_points):
# Point is in local coord, as start and end points
sp = self.start_point
fact_x, fact_y = ratio
if point.x() == sp.x() and fact_x < 0:
new_x = point.x()
elif 0 <= fact_x < 1:
new_x = point.x() - (delta_x * fact_x)
else:
new_x = point.x() - delta_x
if point.y() == sp.y() and fact_y < 0:
new_y = point.y()
elif 0 <= fact_y < 1:
new_y = point.y() - (delta_y * fact_y)
else:
new_y = point.y() - delta_y

self._middle_points.append(
self.parent.mapToScene(QPointF(new_x, new_y)))

self.reshape()
self.update() # force a repaint
```

## When the parent block is moved

Apply a similar move..