Commit 19b08db8 authored by Alice Binder's avatar Alice Binder
Browse files

link changing

parent f70d677d
Pipeline #2392 failed with stages
import React from 'react';
import {
AbstractLinkFactory,
BaseWidgetProps,
DefaultLabelModel,
DefaultLinkFactory,
DefaultLinkModel,
DefaultLinkWidget,
DefaultPortModel, DiagramEngine, PointModel, Toolkit
} from 'storm-react-diagrams';
import {SubQuantityLinkWidget} from "./SubQuantityLink";
export class CommonLinkModel extends DefaultLinkModel {
width: number;
color: string;
curvyness: number;
linktype: string;
constructor(){
super("link-common");
this.width = 3;
this.curvyness = 0;
this.linktype = "common";
}
serialize() {
return _.merge(super.serialize(), {
width: this.width,
color: this.color,
curvyness: this.curvyness,
linktype: this.linktype
});
}
deSerialize(ob, engine: DiagramEngine) {
super.deSerialize(ob, engine);
this.color = ob.color;
this.width = ob.width;
this.curvyness = ob.curvyness;
this.linktype = ob.linktype;
}
setLabel(str: string){
......@@ -24,10 +49,9 @@ export class CommonLinkModel extends DefaultLinkModel {
} else {
this.addLabel(str);
}
}
}
/*
export interface CommonLinkProps extends BaseWidgetProps {
color?: string;
width?: number;
......@@ -40,10 +64,28 @@ export interface CommonLinkProps extends BaseWidgetProps {
export interface CommonLinkState {
selected: boolean;
}
*/
export class CommonLinkWidget extends DefaultLinkWidget {
// DOM references to the label and paths (if label is given), used to calculate dynamic positioning
/*
refLabels: { [id: string]: HTMLElement };
refPaths: SVGPathElement[];
constructor(props: CommonLinkProps) {
super("common-link", props);
this.refLabels = {};
this.refPaths = [];
this.state = {
selected: false
};
}
*/
getAngle(px1, py1, px2, py2) {
const x = px2-px1;
const y = py2-py1;
......@@ -59,7 +101,7 @@ export class CommonLinkWidget extends DefaultLinkWidget {
return angle;
}
generatePoint(pointIndex: number): JSX.Element {
generateEnd(pointIndex: number): JSX.Element {
let x = this.props.link.points[pointIndex].x;
let y = this.props.link.points[pointIndex].y;
const pointOne = this.props.link.points[pointIndex-1];
......@@ -93,19 +135,184 @@ export class CommonLinkWidget extends DefaultLinkWidget {
/>
<polygon
x={x-20}
y={y+12}
transform={`rotate(${angle}, ${x}, ${y})`}
points={`${x - 10},${y - 8} ${x+3},${y} ${x - 10},${y + 8}`}
x={this.props.link.points[pointIndex].x-20}
y={this.props.link.points[pointIndex].y+12}
transform={`rotate(${angle}, ${this.props.link.points[pointIndex].x}, ${this.props.link.points[pointIndex].y})`}
points={`${this.props.link.points[pointIndex].x - 10},${this.props.link.points[pointIndex].y - 8} ${this.props.link.points[pointIndex].x+3},${this.props.link.points[pointIndex].y} ${this.props.link.points[pointIndex].x - 10},${this.props.link.points[pointIndex].y + 8}`}
/>
</g>
);
}
generatePoint(pointIndex: number): JSX.Element {
let x = this.props.link.points[pointIndex].x;
let y = this.props.link.points[pointIndex].y;
return (
<g key={"point-" + this.props.link.points[pointIndex].id}>
<circle
onMouseLeave={() => {
this.setState({ selected: false });
}}
onMouseEnter={() => {
this.setState({ selected: true });
}}
data-id={this.props.link.points[pointIndex].id}
data-linkid={this.props.link.id}
cx={x}
cy={y}
r={5}
opacity={.5}
className={
"point " +
this.bem("__point") +
(this.props.link.points[pointIndex].isSelected() ? this.bem("--point-selected") : "")
}
/>
</g>
);
}
render() {
const { diagramEngine } = this.props;
if (!diagramEngine.nodesRendered) {
return null;
}
//ensure id is present for all points on the path
var points = this.props.link.points;
var paths = [];
if (this.isSmartRoutingApplicable()) {
// first step: calculate a direct path between the points being linked
const directPathCoords = this.pathFinding.calculateDirectPath(_.first(points), _.last(points));
const routingMatrix = diagramEngine.getRoutingMatrix();
// now we need to extract, from the routing matrix, the very first walkable points
// so they can be used as origin and destination of the link to be created
const smartLink = this.pathFinding.calculateLinkStartEndCoords(routingMatrix, directPathCoords);
if (smartLink) {
const { start, end, pathToStart, pathToEnd } = smartLink;
// second step: calculate a path avoiding hitting other elements
const simplifiedPath = this.pathFinding.calculateDynamicPath(
routingMatrix,
start,
end,
pathToStart,
pathToEnd
);
paths.push(
//smooth: boolean, extraProps: any, id: string | number, firstPoint: PointModel, lastPoint: PointModel
this.generateLink(
Toolkit.generateDynamicPath(simplifiedPath),
{
onMouseDown: event => {
this.addPointToLink(event, 1);
}
},
"0"
)
);
}
}
// true when smart routing was skipped or not enabled.
// See @link{#isSmartRoutingApplicable()}.
if (paths.length === 0) {
if (points.length === 2) {
var isHorizontal = Math.abs(points[0].x - points[1].x) > Math.abs(points[0].y - points[1].y);
var xOrY = isHorizontal ? "x" : "y";
//draw the smoothing
//if the points are too close, just draw a straight line
var margin = 50;
if (Math.abs(points[0][xOrY] - points[1][xOrY]) < 50) {
margin = 5;
}
var pointLeft = points[0];
var pointRight = points[1];
//some defensive programming to make sure the smoothing is
//always in the right direction
if (pointLeft[xOrY] > pointRight[xOrY]) {
pointLeft = points[1];
pointRight = points[0];
}
paths.push(
this.generateLink(
Toolkit.generateCurvePath(pointLeft, pointRight, this.props.link.curvyness),
{
onMouseDown: event => {
this.addPointToLink(event, 1);
}
},
"0"
)
);
// draw the link as dangeling
//if (this.props.link.targetPort === null) {
// paths.push(this.generatePoint(1));
//}
paths.push(this.generateEnd(1));
} else {
//draw the multiple anchors and complex line instead
for (let j = 0; j < points.length - 1; j++) {
paths.push(
this.generateLink(
Toolkit.generateLinePath(points[j], points[j + 1]),
{
"data-linkid": this.props.link.id,
"data-point": j,
onMouseDown: (event: MouseEvent) => {
this.addPointToLink(event, j + 1);
}
},
j
)
);
}
//render the circles
for (var i = 1; i < points.length - 1; i++) {
paths.push(this.generatePoint(i));
}
paths.push(this.generateEnd(points.length - 1));
if (this.props.link.targetPort === null) {
paths.push(this.generateEnd(points.length - 1));
}
}
}
this.refPaths = [];
return (
<g {...this.getProps()}>
{paths}
{_.map(this.props.link.labels, labelModel => {
return this.generateLabel(labelModel);
})}
</g>
);
}
}
export class CommonLinkFactory extends DefaultLinkFactory{
/*
CommonLinkWidget.defaultProps = {
color: "black",
width: 3,
link: null,
engine: null,
smooth: false,
diagramEngine: null
}
*/
export class CommonLinkFactory extends AbstractLinkFactory<CommonLinkModel>{
constructor(){
super();
this.type = "link-common";
......@@ -116,15 +323,21 @@ export class CommonLinkFactory extends DefaultLinkFactory{
}
generateReactWidget(diagramEngine: DiagramEngine, link: CommonLinkModel): JSX.Element {
return React.createElement(CommonLinkWidget, {
link: link,
diagramEngine: diagramEngine
});
if (link.linktype == "common"){
return React.createElement(CommonLinkWidget, {
link: link,
diagramEngine: diagramEngine
});
} else if (link.linktype == "subquantity"){
return React.createElement(SubQuantityLinkWidget, {
link: link,
diagramEngine: diagramEngine
});
}
}
generateLinkSegment(model: CommonLinkModel, widget: CommonLinkWidget, selected: boolean, path:string){
var markerId= Toolkit.UID();
var markerEndUrl = "url(#"+markerId+")";
return (
<path className={selected ? "link-common--path-selected" : "link-common"}
......@@ -134,7 +347,6 @@ export class CommonLinkFactory extends DefaultLinkFactory{
strokeWidth={model.width}
stroke="black"
d={path}
markerEnd={markerEndUrl}
/>
);
......
......@@ -2,6 +2,7 @@ import * as _ from "lodash";
import { LinkModel, DiagramEngine, PortModel } from "storm-react-diagrams";
import {CommonLinkModel} from "../../links/CommonLink";
import {MaterialLinkModel} from "../../links/MaterialLink";
import {ComponentLinkModel} from "../../links/ComponentLink";
export class NodeKindPortModel extends PortModel {
constructor(pos: string = "port") {
......@@ -21,6 +22,6 @@ export class NodeKindPortModel extends PortModel {
}
createLinkModel(): LinkModel {
return new MaterialLinkModel();
return new CommonLinkModel();
}
}
\ No newline at end of file
import {
AbstractLabelFactory,
AbstractLinkFactory, AbstractNodeFactory,
AbstractPortFactory,
DiagramEngine,
Toolkit
} from "storm-react-diagrams";
import {CustomDiagramModel} from "./CustomDiagramModel";
export class CustomDiagramEngine extends DiagramEngine{
nodeFactories: { [s: string]: AbstractNodeFactory };
linkFactories: { [s: string]: AbstractLinkFactory };
portFactories: { [s: string]: AbstractPortFactory };
labelFactories: { [s: string]: AbstractLabelFactory };
diagramModel: CustomDiagramModel;
canvas: Element;
paintableWidgets: {};
linksThatHaveInitiallyRendered: {};
nodesRendered: boolean;
maxNumberPointsPerLink: number;
smartRouting: boolean;
constructor() {
super();
this.diagramModel = new CustomDiagramModel();
this.nodeFactories = {};
this.linkFactories = {};
this.portFactories = {};
this.labelFactories = {};
this.canvas = null;
this.paintableWidgets = null;
this.linksThatHaveInitiallyRendered = {};
if (Toolkit.TESTING) {
Toolkit.TESTING_UID = 0;
//pop it onto the window so our E2E helpers can find it
//if (window) {
// (window as any)["diagram_instance"] = this;
//}
}
}
isSmartRoutingEnabled(): boolean{
return false;
}
}
\ No newline at end of file
import {
AbstractLabelFactory,
AbstractLinkFactory,
AbstractNodeFactory,
AbstractPortFactory,
DiagramModel, LinkModel, NodeModel,
Toolkit
} from "storm-react-diagrams";
export class CustomDiagramModel extends DiagramModel {
selectedLink: string;
//models
links: { [s: string]: LinkModel };
nodes: { [s: string]: NodeModel };
//control variables
offsetX: number;
offsetY: number;
zoom: number;
rendered: boolean;
gridSize: number;
constructor() {
super();
this.links = {};
this.nodes = {};
this.offsetX = 0;
this.offsetY = 0;
this.zoom = 100;
this.rendered = false;
this.gridSize = 0;
this.selectedLink = "common";
}
}
\ No newline at end of file
......@@ -54,18 +54,21 @@ import {NodeSubkindModel} from "../components/nodes/subkind/NodeSubkindModel";
import {CharacterizationLinkFactory} from "../components/links/CharacterizationLink";
import {ComponentLinkFactory} from "../components/links/ComponentLink";
import {DerivationLinkFactory} from "../components/links/DerivationLink";
import {FormalLinkFactory} from "../components/links/FormalLink";
import {FormalLinkFactory, FormalLinkModel} from "../components/links/FormalLink";
import {MaterialLinkFactory} from "../components/links/MaterialLink";
import {MediationLinkFactory} from "../components/links/MediationLink";
import {MemberLinkFactory} from "../components/links/MemberLink";
import {SubCollectionLinkFactory} from "../components/links/SubCollectionLink";
import {SubQuantityLinkFactory} from "../components/links/SubQuantityLink";
import {MenuPanel} from "../panel/MenuPanel";
import {CustomDiagramModel} from "./CustomDiagramModel.js";
export class DiagramCanvas extends React.Component {
constructor(props){
super(props);
this.state = {selectedlink: 'common'};
this.handleChange = this.handleChange.bind(this);
}
registerFactories(){
......@@ -122,76 +125,42 @@ export class DiagramCanvas extends React.Component {
this.registerFactories();
this.engine.setDiagramModel(new DiagramModel());
this.engine.setDiagramModel(new CustomDiagramModel());
const kind = new NodeKindModel();
kind.addAttribute(new AttributeObject("me","string"));
kind.addAttribute(new AttributeObject("you","string"));
kind.addAttribute(new AttributeObject("we","string"));
/*
const test = new DefaultNodeModel();
test.addPort(new CommonPortModel(true,'whatever',''));
this.engine.getDiagramModel().addNode(test);
*/
this.engine.getDiagramModel().addNode(kind);
}
handleChange(event){
this.setState({selectedlink: event.target.value});
this.engine.getDiagramModel().selectedLink = event.target.value;
}
render() {
return (
<div>
<select value={this.state.selectedlink} onChange={this.handleChange}>
<option value="common">Common</option>
<option value="characterization">Characterization</option>
<option value="component">Component</option>
<option value="derivation">Derivation</option>
<option value="formal">Formal</option>
<option value="material">Material</option>
<option value="mediation">Mediation</option>
<option value="member">Member</option>
<option value="subcollection">SubCollection</option>
<option value="subquantity">SubQuantity</option>
</select>
<button onClick={event => {
console.log(JSON.stringify(this.engine.diagramModel.serializeDiagram()));
}}>Serialize</button>
<button onClick={event => {
let str = prompt("Enter JSON");
this.engine.registerNodeFactory(new DefaultNodeFactory());
this.engine.registerNodeFactory(new NodeCategoryFactory());
this.engine.registerNodeFactory(new NodeCollectiveFactory());
this.engine.registerNodeFactory(new NodeKindFactory());
this.engine.registerNodeFactory(new NodeMixinFactory());
this.engine.registerNodeFactory(new NodeModeFactory());
this.engine.registerNodeFactory(new NodePhaseFactory());
this.engine.registerNodeFactory(new NodeQualityFactory());
this.engine.registerNodeFactory(new NodeQuantityFactory());
this.engine.registerNodeFactory(new NodeRelatorFactory());
this.engine.registerNodeFactory(new NodeRoleFactory());
this.engine.registerNodeFactory(new NodeRoleMixinFactory());
this.engine.registerNodeFactory(new NodeSubkindFactory());
this.engine.registerLinkFactory(new DefaultLinkFactory());
this.engine.registerLinkFactory(new CommonLinkFactory());
this.engine.registerLinkFactory(new CharacterizationLinkFactory());
this.engine.registerLinkFactory(new ComponentLinkFactory());
this.engine.registerLinkFactory(new DerivationLinkFactory());
this.engine.registerLinkFactory(new FormalLinkFactory());
this.engine.registerLinkFactory(new MaterialLinkFactory());
this.engine.registerLinkFactory(new MediationLinkFactory());
this.engine.registerLinkFactory(new MemberLinkFactory());
this.engine.registerLinkFactory(new SubCollectionLinkFactory());
this.engine.registerLinkFactory(new SubQuantityLinkFactory());
this.engine.registerLabelFactory(new DefaultLabelFactory());
this.engine.registerPortFactory(new DefaultPortFactory());
this.engine.registerPortFactory(new CommonPortFactory());
this.engine.registerPortFactory(new NodeCategoryPortFactory());
this.engine.registerPortFactory(new NodeCollectivePortFactory());
this.engine.registerPortFactory(new NodeKindPortFactory());
this.engine.registerPortFactory(new NodeMixinPortFactory());
this.engine.registerPortFactory(new NodeModePortFactory());
this.engine.registerPortFactory(new NodePhasePortFactory());
this.engine.registerPortFactory(new NodeQualityPortFactory());
this.engine.registerPortFactory(new NodeQuantityPortFactory());
this.engine.registerPortFactory(new NodeRelatorPortFactory());
this.engine.registerPortFactory(new NodeRolePortFactory());
this.engine.registerPortFactory(new NodeRoleMixinPortFactory());
this.engine.registerPortFactory(new NodeSubkindPortFactory());
this.registerFactories();
let model = new DiagramModel();
model.deSerializeDiagram(JSON.parse(str), this.engine);
this.engine.setDiagramModel(model);
......@@ -238,7 +207,7 @@ export class DiagramCanvas extends React.Component {
onDragOver={event => {
event.preventDefault();
}}>
<DiagramWidget diagramEngine={this.engine}/>
<DiagramWidget diagramEngine={this.engine} allowLooseLinks={false} smartRouting={false}/>
</div>
</div>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment