sprotty
Version:
A next-gen framework for graphical views
239 lines • 9.8 kB
JavaScript
"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