UNPKG

sprotty

Version:

A next-gen framework for graphical views

239 lines 9.8 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 ********************************************************************************/ Object.defineProperty(exports, "__esModule", { value: true }); exports.SGraphIndex = exports.SCompartmentImpl = exports.SLabelImpl = exports.SEdgeImpl = exports.SPortImpl = exports.SNodeImpl = exports.SGraphImpl = void 0; const geometry_1 = require("sprotty-protocol/lib/utils/geometry"); const smodel_1 = require("../base/model/smodel"); const model_1 = require("../features/bounds/model"); const model_2 = require("../features/edge-layout/model"); const delete_1 = require("../features/edit/delete"); const model_3 = require("../features/edit/model"); const model_4 = require("../features/fade/model"); const model_5 = require("../features/hover/model"); const model_6 = require("../features/move/model"); const model_7 = require("../features/routing/model"); const model_8 = require("../features/select/model"); const viewport_root_1 = require("../features/viewport/viewport-root"); const iterable_1 = require("../utils/iterable"); /** * Root element for graph-like models. */ class SGraphImpl extends viewport_root_1.ViewportRootElementImpl { constructor(index = new SGraphIndex()) { super(index); } } exports.SGraphImpl = SGraphImpl; /** * Model element class for nodes, which are the main entities in a graph. A node can be connected to * another node via an SEdge. Such a connection can be direct, i.e. the node is the source or target of * the edge, or indirect through a port, i.e. it contains an SPort which is the source or target of the edge. */ class SNodeImpl extends model_7.SConnectableElementImpl { constructor() { super(...arguments); this.selected = false; this.hoverFeedback = false; this.opacity = 1; } canConnect(routable, role) { return this.children.find(c => c instanceof SPortImpl) === undefined; } get incomingEdges() { const index = this.index; if (index instanceof SGraphIndex) { return index.getIncomingEdges(this); } const allEdges = this.index.all().filter(e => e instanceof SEdgeImpl); return allEdges.filter(e => e.targetId === this.id); } get outgoingEdges() { const index = this.index; if (index instanceof SGraphIndex) { return index.getOutgoingEdges(this); } const allEdges = this.index.all().filter(e => e instanceof SEdgeImpl); return allEdges.filter(e => e.sourceId === this.id); } } exports.SNodeImpl = SNodeImpl; SNodeImpl.DEFAULT_FEATURES = [model_7.connectableFeature, delete_1.deletableFeature, model_8.selectFeature, model_1.boundsFeature, model_6.moveFeature, model_1.layoutContainerFeature, model_4.fadeFeature, model_5.hoverFeedbackFeature, model_5.popupFeature]; /** * A port is a connection point for edges. It should always be contained in an SNode. */ class SPortImpl extends model_7.SConnectableElementImpl { constructor() { super(...arguments); this.selected = false; this.hoverFeedback = false; this.opacity = 1; } get incomingEdges() { const index = this.index; if (index instanceof SGraphIndex) { return index.getIncomingEdges(this); } return super.incomingEdges.filter(e => e instanceof SEdgeImpl); } get outgoingEdges() { const index = this.index; if (index instanceof SGraphIndex) { return index.getOutgoingEdges(this); } return super.outgoingEdges.filter(e => e instanceof SEdgeImpl); } } exports.SPortImpl = SPortImpl; SPortImpl.DEFAULT_FEATURES = [model_7.connectableFeature, model_8.selectFeature, model_1.boundsFeature, model_4.fadeFeature, model_5.hoverFeedbackFeature]; /** * Model element class for edges, which are the connectors in a graph. An edge has a source and a target, * each of which can be either a node or a port. The source and target elements are referenced via their * ids and can be resolved with the index stored in the root element. */ class SEdgeImpl extends model_7.SRoutableElementImpl { constructor() { super(...arguments); this.selected = false; this.hoverFeedback = false; this.opacity = 1; } } exports.SEdgeImpl = SEdgeImpl; SEdgeImpl.DEFAULT_FEATURES = [model_3.editFeature, delete_1.deletableFeature, model_8.selectFeature, model_4.fadeFeature, model_5.hoverFeedbackFeature]; /** * A label can be attached to a node, edge, or port, and contains some text to be rendered in its view. */ class SLabelImpl extends model_1.SShapeElementImpl { constructor() { super(...arguments); this.selected = false; this.alignment = geometry_1.Point.ORIGIN; this.opacity = 1; } } exports.SLabelImpl = SLabelImpl; SLabelImpl.DEFAULT_FEATURES = [model_1.boundsFeature, model_1.alignFeature, model_1.layoutableChildFeature, model_2.edgeLayoutFeature, model_4.fadeFeature]; /** * A compartment is used to group multiple child elements such as labels of a node. Usually a `vbox` * or `hbox` layout is used to arrange these children. */ class SCompartmentImpl extends model_1.SShapeElementImpl { constructor() { super(...arguments); this.opacity = 1; } } exports.SCompartmentImpl = SCompartmentImpl; SCompartmentImpl.DEFAULT_FEATURES = [model_1.boundsFeature, model_1.layoutContainerFeature, model_1.layoutableChildFeature, model_4.fadeFeature]; /** * A specialized model index that tracks outgoing and incoming edges. */ class SGraphIndex extends smodel_1.ModelIndexImpl { constructor() { super(...arguments); this.outgoing = new Map; this.incoming = new Map; } add(element) { super.add(element); if (element instanceof SEdgeImpl) { // Register the edge in the outgoing map if (element.sourceId) { const sourceArr = this.outgoing.get(element.sourceId); if (sourceArr === undefined) this.outgoing.set(element.sourceId, [element]); else sourceArr.push(element); } // Register the edge in the incoming map if (element.targetId) { const targetArr = this.incoming.get(element.targetId); if (targetArr === undefined) this.incoming.set(element.targetId, [element]); else targetArr.push(element); } } } remove(element) { super.remove(element); if (element instanceof SEdgeImpl) { // Remove the edge from the outgoing map const sourceArr = this.outgoing.get(element.sourceId); if (sourceArr !== undefined) { const index = sourceArr.indexOf(element); if (index >= 0) { if (sourceArr.length === 1) this.outgoing.delete(element.sourceId); else sourceArr.splice(index, 1); } } // Remove the edge from the incoming map const targetArr = this.incoming.get(element.targetId); if (targetArr !== undefined) { const index = targetArr.indexOf(element); if (index >= 0) { if (targetArr.length === 1) this.incoming.delete(element.targetId); else targetArr.splice(index, 1); } } } } getAttachedElements(element) { return new iterable_1.FluentIterableImpl(() => ({ outgoing: this.outgoing.get(element.id), incoming: this.incoming.get(element.id), nextOutgoingIndex: 0, nextIncomingIndex: 0 }), (state) => { let index = state.nextOutgoingIndex; if (state.outgoing !== undefined && index < state.outgoing.length) { state.nextOutgoingIndex = index + 1; return { done: false, value: state.outgoing[index] }; } index = state.nextIncomingIndex; if (state.incoming !== undefined) { // Filter out self-loops: edges that are both outgoing and incoming while (index < state.incoming.length) { const edge = state.incoming[index]; if (edge.sourceId !== edge.targetId) { state.nextIncomingIndex = index + 1; return { done: false, value: edge }; } index++; } } return { done: true, value: undefined }; }); } getIncomingEdges(element) { return this.incoming.get(element.id) || []; } getOutgoingEdges(element) { return this.outgoing.get(element.id) || []; } } exports.SGraphIndex = SGraphIndex; //# sourceMappingURL=sgraph.js.map