Skip to content

Commit

Permalink
Add port aware snapper that snaps them to the parent element edge
Browse files Browse the repository at this point in the history
  • Loading branch information
hlxid committed Sep 12, 2023
1 parent fed0302 commit 2829cc7
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 10 deletions.
13 changes: 5 additions & 8 deletions src/features/dfdElements/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,19 @@ import {
withEditLabelFeature,
SLabelView,
SRoutingHandleImpl,
TYPES,
} from "sprotty";
import {
FunctionNodeImpl,
FunctionNodeView,
IONodeImpl,
IONodeView,
StorageNodeImpl,
StorageNodeView,
} from "./nodes";
import { FunctionNodeImpl, FunctionNodeView, IONodeImpl, IONodeView, StorageNodeImpl, StorageNodeView } from "./nodes";
import { ArrowEdgeImpl, ArrowEdgeView } from "./edges";
import { DfdPortImpl, DfdPortView } from "./port";
import { FilledBackgroundLabelView, DfdPositionalLabelView } from "./labels";
import { PortAwareSnapper } from "./portSnapper";

import "./styles.css";

export const dfdElementsModule = new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(TYPES.ISnapper).to(PortAwareSnapper).inSingletonScope();

const context = { bind, unbind, isBound, rebind };
configureModelElement(context, "graph", SGraphImpl, SGraphView);
configureModelElement(context, "node:storage", StorageNodeImpl, StorageNodeView);
Expand Down
4 changes: 2 additions & 2 deletions src/features/dfdElements/nodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ export abstract class DfdNodeImpl extends DynamicChildrenNode implements WithEdi
type: "port:dfd",
id: schema.id + "-port",
size: { width: 6, height: 6 },
position: { x: -3, y: 0 },
position: { x: -6, y: 3 },
behaviour: "test123",
} as SPort,
{
type: "port:dfd",
id: schema.id + "-port2",
size: { width: 6, height: 6 },
position: { x: -3, y: 12 },
position: { x: -6, y: 15 },
behaviour: "test123",
} as SPort,
];
Expand Down
59 changes: 59 additions & 0 deletions src/features/dfdElements/portSnapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { injectable } from "inversify";
import { CenterGridSnapper, ISnapper, SModelElementImpl, SPortImpl, isBoundsAware } from "sprotty";
import { Point } from "sprotty-protocol";

@injectable()
export class PortAwareSnapper implements ISnapper {
private readonly gridSnapper = new CenterGridSnapper();

private snapPort(position: Point, element: SPortImpl): Point {
const parentElement = element.parent;

if (!isBoundsAware(parentElement)) {
// Cannot get the parent size, just return the original position and don't snap
return position;
}

const parentBounds = parentElement.bounds;

// Clamp the position to be inside the parent bounds
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);

const clampX = clamp(position.x, 0, parentBounds.width);
const clampY = clamp(position.y, 0, parentBounds.height);

// Determine the closest edge
const distances = [
{ x: clampX, y: 0 }, // Top edge
{ x: 0, y: clampY }, // Left edge
{ x: parentBounds.width, y: clampY }, // Right edge
{ x: clampX, y: parentBounds.height }, // Bottom edge
];

const closestEdge = distances.reduce((prev, curr) =>
Math.hypot(curr.x - position.x, curr.y - position.y) < Math.hypot(prev.x - position.x, prev.y - position.y)
? curr
: prev,
);

// Move the port from being on top of the edge on the side of the edge.
// Before this step x and y are the point of the top left point of the port inside the parent element.
// After this step the port will be moved on the side of the edge.
// So if it is on the top edge it will be above the edge and contact it at the bottom.
// If it is on the left edge it will be on the left of the edge and contact it on the right.
// For bottom and right we don't need to do anything because the port is already on the side of the edge.
// The movement is scaled by the position to make a smooth transition in the four corners possible.
const snappedX = closestEdge.x - element.bounds.width * (1 - closestEdge.x / parentBounds.width);
const snappedY = closestEdge.y - element.bounds.height * (1 - closestEdge.y / parentBounds.height);

return { x: snappedX, y: snappedY };
}

snap(position: Point, element: SModelElementImpl): Point {
if (element instanceof SPortImpl) {
return this.snapPort(position, element);
} else {
return this.gridSnapper.snap(position, element);
}
}
}

0 comments on commit 2829cc7

Please sign in to comment.