sprotty
Version:
A next-gen framework for graphical views
278 lines • 15 kB
JavaScript
;
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EdgeLayoutPostprocessor = void 0;
const inversify_1 = require("inversify");
const geometry_1 = require("sprotty-protocol/lib/utils/geometry");
const smodel_1 = require("../../base/model/smodel");
const vnode_utils_1 = require("../../base/views/vnode-utils");
const sgraph_1 = require("../../graph/sgraph");
const model_1 = require("../bounds/model");
const model_2 = require("./model");
const routing_1 = require("../routing/routing");
const types_1 = require("../../base/types");
const model_3 = require("../move/model");
let EdgeLayoutPostprocessor = class EdgeLayoutPostprocessor {
/**
* Decorates the vnode with the appropriate transformation based on the element's placement and bounds.
* @param vnode - The vnode to decorate.
* @param element - The SModelElementImpl to decorate.
* @returns The decorated vnode.
*/
decorate(vnode, element) {
var _a, _b, _c, _d;
if ((0, model_2.isEdgeLayoutable)(element) && element.parent instanceof sgraph_1.SEdgeImpl) {
if (element.bounds !== geometry_1.Bounds.EMPTY) {
const actualBounds = element.bounds;
const hasOwnPlacement = (0, model_2.checkEdgePlacement)(element);
const placement = this.getEdgePlacement(element);
const edge = element.parent;
const position = Math.min(1, Math.max(0, placement.position));
const router = this.edgeRouterRegistry.get(edge.routerKind);
// point on edge derived from edgePlacement.position
const pointOnEdge = router.pointAt(edge, position);
let transform = '';
// get the relative position on segment. This can be later changed if the moveMode is set to 'edge'.
let derivativeOnEdge = router.derivativeAt(edge, position);
;
// Check if edgeplacement is set. If not the label is freely movable if movefeature is enabled for such labels.
if (pointOnEdge) {
if (hasOwnPlacement) {
switch (placement.moveMode) {
case 'edge':
// Find orthogonal intersection point on edge and use it as the label's position
const orthogonalPoint = router.findOrthogonalIntersection(edge, geometry_1.Point.add(pointOnEdge, actualBounds));
if (orthogonalPoint) {
derivativeOnEdge = orthogonalPoint.derivative;
transform += `translate(${orthogonalPoint.point.x}, ${orthogonalPoint.point.y})`;
}
break;
case 'free':
// Calculation of potential free movement. Just add the actual bounds to the point on edge.
transform += `translate(${((_a = pointOnEdge === null || pointOnEdge === void 0 ? void 0 : pointOnEdge.x) !== null && _a !== void 0 ? _a : 0) + actualBounds.x}, ${((_b = pointOnEdge === null || pointOnEdge === void 0 ? void 0 : pointOnEdge.y) !== null && _b !== void 0 ? _b : 0) + actualBounds.y})`;
;
break;
case 'none':
transform += `translate(${pointOnEdge.x}, ${pointOnEdge.y})`;
break;
default:
this.logger.error({}, 'No moveMode set for edge label. Skipping edge placement.');
break;
}
if (derivativeOnEdge) {
const angle = (0, geometry_1.toDegrees)(Math.atan2(derivativeOnEdge.y, derivativeOnEdge.x));
if (placement.rotate) {
let flippedAngle = angle;
// Flip angle if it exceeds 90 degrees
if (Math.abs(angle) > 90) {
if (angle < 0)
flippedAngle += 180;
else if (angle > 0)
flippedAngle -= 180;
}
transform += ` rotate(${flippedAngle})`;
// Get rotated alignment based on flipped angle
const alignment = this.getRotatedAlignment(element, placement, flippedAngle !== angle);
transform += ` translate(${alignment.x}, ${alignment.y})`;
}
else {
// Get alignment based on angle
const alignment = this.getAlignment(element, placement, angle);
transform += ` translate(${alignment.x}, ${alignment.y})`;
}
}
}
else {
// if the element is moveable and no placement is specified, the label is freely movable (i.e. moveMode = 'free').
// Otherwise it is fixed to its position (i.e. moveMode = 'none').
if ((0, model_3.isMoveable)(element)) {
transform += `translate(${((_c = pointOnEdge === null || pointOnEdge === void 0 ? void 0 : pointOnEdge.x) !== null && _c !== void 0 ? _c : 0) + actualBounds.x}, ${((_d = pointOnEdge === null || pointOnEdge === void 0 ? void 0 : pointOnEdge.y) !== null && _d !== void 0 ? _d : 0) + actualBounds.y})`;
;
}
else {
transform += `translate(${pointOnEdge.x}, ${pointOnEdge.y})`;
}
}
}
(0, vnode_utils_1.setAttr)(vnode, 'transform', transform);
}
}
return vnode;
}
getRotatedAlignment(element, placement, flip) {
let x = (0, model_1.isAlignable)(element) ? element.alignment.x : 0;
let y = (0, model_1.isAlignable)(element) ? element.alignment.y : 0;
const bounds = element.bounds;
if (placement.side === 'on')
return { x: x - 0.5 * bounds.height, y: y - 0.5 * bounds.height };
if (flip) {
if (placement.position < 0.3333333)
x -= bounds.width + placement.offset;
else if (placement.position < 0.6666666)
x -= 0.5 * bounds.width;
else
x += placement.offset;
switch (placement.side) {
case 'left':
case 'bottom':
y -= placement.offset + bounds.height;
break;
case 'right':
case 'top':
y += placement.offset;
}
}
else {
if (placement.position < 0.3333333)
x += placement.offset;
else if (placement.position < 0.6666666)
x -= 0.5 * bounds.width;
else
x -= bounds.width + placement.offset;
switch (placement.side) {
case 'right':
case 'bottom':
y += -placement.offset - bounds.height;
break;
case 'left':
case 'top':
y += placement.offset;
}
}
return { x, y };
}
getEdgePlacement(element) {
let current = element;
const allPlacements = [];
while (current !== undefined) {
const placement = current.edgePlacement;
if (placement !== undefined)
allPlacements.push(placement);
if (current instanceof smodel_1.SChildElementImpl)
current = current.parent;
else
break;
}
const edgePlacement = allPlacements.reverse().reduce((a, b) => { return Object.assign(Object.assign({}, a), b); }, model_2.DEFAULT_EDGE_PLACEMENT);
if (!edgePlacement.moveMode) {
edgePlacement.moveMode = (0, model_3.isMoveable)(element) ? 'edge' : 'none';
}
return edgePlacement;
}
getAlignment(label, placement, angle) {
const bounds = label.bounds;
const x = (0, model_1.isAlignable)(label) ? label.alignment.x - bounds.width : 0;
const y = (0, model_1.isAlignable)(label) ? label.alignment.y - bounds.height : 0;
if (placement.side === 'on') {
return { x: x + 0.5 * bounds.width, y: y + 0.5 * bounds.height };
}
const quadrant = this.getQuadrant(angle);
const midLeft = { x: placement.offset, y: y + 0.5 * bounds.height };
const topLeft = { x: placement.offset, y: y + bounds.height + placement.offset };
const topRight = { x: -bounds.width - placement.offset, y: y + bounds.height + placement.offset };
const midRight = { x: -bounds.width - placement.offset, y: y + 0.5 * bounds.height };
const bottomRight = { x: -bounds.width - placement.offset, y: y - placement.offset };
const bottomLeft = { x: placement.offset, y: y - placement.offset };
switch (placement.side) {
case 'left':
switch (quadrant.orientation) {
case 'west':
return geometry_1.Point.linear(topLeft, topRight, quadrant.position);
case 'north':
return geometry_1.Point.linear(topRight, bottomRight, quadrant.position);
case 'east':
return geometry_1.Point.linear(bottomRight, bottomLeft, quadrant.position);
case 'south':
return geometry_1.Point.linear(bottomLeft, topLeft, quadrant.position);
}
break;
case 'right':
switch (quadrant.orientation) {
case 'west':
return geometry_1.Point.linear(bottomRight, bottomLeft, quadrant.position);
case 'north':
return geometry_1.Point.linear(bottomLeft, topLeft, quadrant.position);
case 'east':
return geometry_1.Point.linear(topLeft, topRight, quadrant.position);
case 'south':
return geometry_1.Point.linear(topRight, bottomRight, quadrant.position);
}
break;
case 'top':
switch (quadrant.orientation) {
case 'west':
return geometry_1.Point.linear(bottomRight, bottomLeft, quadrant.position);
case 'north':
return this.linearFlip(bottomLeft, midLeft, midRight, bottomRight, quadrant.position);
case 'east':
return geometry_1.Point.linear(bottomRight, bottomLeft, quadrant.position);
case 'south':
return this.linearFlip(bottomLeft, midLeft, midRight, bottomRight, quadrant.position);
}
break;
case 'bottom':
switch (quadrant.orientation) {
case 'west':
return geometry_1.Point.linear(topLeft, topRight, quadrant.position);
case 'north':
return this.linearFlip(topRight, midRight, midLeft, topLeft, quadrant.position);
case 'east':
return geometry_1.Point.linear(topLeft, topRight, quadrant.position);
case 'south':
return this.linearFlip(topRight, midRight, midLeft, topLeft, quadrant.position);
}
break;
}
return { x: 0, y: 0 };
}
getQuadrant(angle) {
if (Math.abs(angle) > 135)
return { orientation: 'west', position: (angle > 0 ? angle - 135 : angle + 225) / 90 };
else if (angle < -45)
return { orientation: 'north', position: (angle + 135) / 90 };
else if (angle < 45)
return { orientation: 'east', position: (angle + 45) / 90 };
else
return { orientation: 'south', position: (angle - 45) / 90 };
}
linearFlip(p0, p1, p2, p3, position) {
return position < 0.5 ? geometry_1.Point.linear(p0, p1, 2 * position) : geometry_1.Point.linear(p2, p3, 2 * position - 1);
}
postUpdate() { }
};
exports.EdgeLayoutPostprocessor = EdgeLayoutPostprocessor;
__decorate([
(0, inversify_1.inject)(routing_1.EdgeRouterRegistry),
__metadata("design:type", routing_1.EdgeRouterRegistry)
], EdgeLayoutPostprocessor.prototype, "edgeRouterRegistry", void 0);
__decorate([
(0, inversify_1.inject)(types_1.TYPES.ILogger),
__metadata("design:type", Object)
], EdgeLayoutPostprocessor.prototype, "logger", void 0);
exports.EdgeLayoutPostprocessor = EdgeLayoutPostprocessor = __decorate([
(0, inversify_1.injectable)()
], EdgeLayoutPostprocessor);
//# sourceMappingURL=edge-layout.js.map