gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
158 lines (157 loc) • 6.9 kB
JavaScript
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
/*
* This is an extension and not part of the main GoJS library.
* Note that the API for this class may change with any version, even point releases.
* If you intend to use an extension in production, you should copy the code to your own source directory.
* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
*/
import * as go from '../release/go-module.js';
/**
* The OrthogonalLinkReshapingTool class lets a user drag a tool handle along the link segment, which will move the whole segment.
*
* If you want to experiment with this extension, try the <a href="../../extensionsTS/OrthogonalLinkReshaping.html">Orthogonal Link Reshaping</a> sample.
* @category Tool Extension
*/
export class OrthogonalLinkReshapingTool extends go.LinkReshapingTool {
/**
* Constructs an OrthogonalLinkReshapingTool and sets the name for the tool.
*/
constructor() {
super();
this._alreadyAddedPoint = false;
this.name = 'OrthogonalLinkReshaping';
}
/**
* @hidden @internal
* For orthogonal, straight links, create the handles and set reshaping behavior.
*/
makeAdornment(pathshape) {
const link = pathshape.part;
// add all normal handles first
const adornment = super.makeAdornment(pathshape);
// add long reshaping handles for orthogonal, straight links
if (link !== null && link.isOrthogonal && link.curve !== go.Link.Bezier) {
const firstindex = link.firstPickIndex + (link.resegmentable ? 0 : 1);
const lastindex = link.lastPickIndex - (link.resegmentable ? 0 : 1);
for (let i = firstindex; i < lastindex; i++) {
this.makeSegmentDragHandle(link, adornment, i);
}
}
return adornment;
}
/**
* This stops the current reshaping operation and updates any link handles.
*/
doDeactivate() {
this._alreadyAddedPoint = false;
// when we finish, recreate adornment to ensure proper reshaping behavior/cursor
const link = this.adornedLink;
if (link !== null && link.isOrthogonal && link.curve !== go.Link.Bezier) {
const pathshape = link.path;
if (pathshape !== null) {
const adornment = this.makeAdornment(pathshape);
if (adornment !== null) {
link.addAdornment(this.name, adornment);
adornment.location = link.position;
}
}
}
super.doDeactivate();
}
/**
* Change the route of the {@link #adornedLink} by moving the segment corresponding to the current
* {@link #handle} to be at the given {@link Point}.
*/
reshape(newpt) {
const link = this.adornedLink;
// identify if the handle being dragged is a segment dragging handle
if (link !== null && link.isOrthogonal && link.curve !== go.Link.Bezier && this.handle !== null && this.handle.toMaxLinks === 999) {
link.startRoute();
let index = this.handle.segmentIndex; // for these handles, firstPickIndex <= index < lastPickIndex
if (!this._alreadyAddedPoint && link.resegmentable) { // only change the number of points if Link.resegmentable
this._alreadyAddedPoint = true;
if (index === link.firstPickIndex) {
link.insertPoint(index, link.getPoint(index).copy());
index++;
this.handle.segmentIndex = index;
}
else if (index === link.lastPickIndex - 1) {
link.insertPoint(index, link.getPoint(index).copy());
}
}
const behavior = this.getReshapingBehavior(this.handle);
if (behavior === go.LinkReshapingTool.Vertical) {
// move segment vertically
link.setPointAt(index, link.getPoint(index - 1).x, newpt.y);
link.setPointAt(index + 1, link.getPoint(index + 2).x, newpt.y);
}
else if (behavior === go.LinkReshapingTool.Horizontal) {
// move segment horizontally
link.setPointAt(index, newpt.x, link.getPoint(index - 1).y);
link.setPointAt(index + 1, newpt.x, link.getPoint(index + 2).y);
}
link.commitRoute();
}
else {
super.reshape(newpt);
}
}
/**
* Create the segment dragging handles.
* There are two parts: one invisible handle that spans the segment, and a visible handle at the middle of the segment.
* These are inserted at the front of the adornment such that the normal handles have priority.
*/
makeSegmentDragHandle(link, adornment, index) {
const a = link.getPoint(index);
let b = link.getPoint(index + 1);
const seglength = Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y));
// determine segment orientation
let orient = '';
if (this.isApprox(a.x, b.x) && this.isApprox(a.y, b.y)) {
b = link.getPoint(index - 1);
if (this.isApprox(a.x, b.x)) {
orient = 'vertical';
}
else if (this.isApprox(a.y, b.y)) {
orient = 'horizontal';
}
}
else {
if (this.isApprox(a.x, b.x)) {
orient = 'vertical';
}
else if (this.isApprox(a.y, b.y)) {
orient = 'horizontal';
}
}
// make an invisible handle along the whole segment
const h = new go.Shape();
h.strokeWidth = 6;
h.opacity = 0;
h.segmentOrientation = go.Link.OrientAlong;
h.segmentIndex = index;
h.segmentFraction = 0.5;
h.toMaxLinks = 999; // set this unsused property to easily identify that we have a segment dragging handle
if (orient === 'horizontal') {
this.setReshapingBehavior(h, go.LinkReshapingTool.Vertical);
h.cursor = 'n-resize';
}
else {
this.setReshapingBehavior(h, go.LinkReshapingTool.Horizontal);
h.cursor = 'w-resize';
}
h.geometryString = 'M 0 0 L ' + seglength + ' 0';
adornment.insertAt(0, h);
}
/**
* Compare two numbers to ensure they are almost equal.
* Used in this class for comparing coordinates of Points.
*/
isApprox(x, y) {
const d = x - y;
return d < 0.5 && d > -0.5;
}
}