UNPKG

sprotty

Version:

A next-gen framework for graphical views

278 lines 15 kB
"use strict"; /******************************************************************************** * 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